Edit

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

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2015-08-25 15:37:48
    Hash : 80ecac9e
    Message : Remove unused mOptimize/mDebug flags from TIntermAggregate These flags were written but they were never read. TEST=compile BUG=angleproject:1116 Change-Id: I41e3e89f13861ebda4828c76c753ca17c74c4358 Reviewed-on: https://chromium-review.googlesource.com/294931 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Zhenyao Mo <zmo@chromium.org> Tested-by: Olli Etuaho <oetuaho@nvidia.com>

  • src/compiler/translator/IntermNode.cpp
  • //
    // Copyright (c) 2002-2014 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.
    //
    
    //
    // Build the intermediate representation.
    //
    
    #include <float.h>
    #include <limits.h>
    #include <math.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <vector>
    
    #include "common/mathutil.h"
    #include "common/matrix_utils.h"
    #include "compiler/translator/HashNames.h"
    #include "compiler/translator/IntermNode.h"
    #include "compiler/translator/SymbolTable.h"
    
    namespace
    {
    
    const float kPi = 3.14159265358979323846f;
    const float kDegreesToRadiansMultiplier = kPi / 180.0f;
    const float kRadiansToDegreesMultiplier = 180.0f / kPi;
    
    TPrecision GetHigherPrecision(TPrecision left, TPrecision right)
    {
        return left > right ? left : right;
    }
    
    bool ValidateMultiplication(TOperator op, const TType &left, const TType &right)
    {
        switch (op)
        {
          case EOpMul:
          case EOpMulAssign:
            return left.getNominalSize() == right.getNominalSize() &&
                   left.getSecondarySize() == right.getSecondarySize();
          case EOpVectorTimesScalar:
          case EOpVectorTimesScalarAssign:
            return true;
          case EOpVectorTimesMatrix:
            return left.getNominalSize() == right.getRows();
          case EOpVectorTimesMatrixAssign:
            return left.getNominalSize() == right.getRows() &&
                   left.getNominalSize() == right.getCols();
          case EOpMatrixTimesVector:
            return left.getCols() == right.getNominalSize();
          case EOpMatrixTimesScalar:
          case EOpMatrixTimesScalarAssign:
            return true;
          case EOpMatrixTimesMatrix:
            return left.getCols() == right.getRows();
          case EOpMatrixTimesMatrixAssign:
            return left.getCols() == right.getCols() &&
                   left.getRows() == right.getRows();
    
          default:
            UNREACHABLE();
            return false;
        }
    }
    
    bool CompareStructure(const TType& leftNodeType,
                          const TConstantUnion *rightUnionArray,
                          const TConstantUnion *leftUnionArray);
    
    bool CompareStruct(const TType &leftNodeType,
                       const TConstantUnion *rightUnionArray,
                       const TConstantUnion *leftUnionArray)
    {
        const TFieldList &fields = leftNodeType.getStruct()->fields();
    
        size_t structSize = fields.size();
        size_t index = 0;
    
        for (size_t j = 0; j < structSize; j++)
        {
            size_t size = fields[j]->type()->getObjectSize();
            for (size_t i = 0; i < size; i++)
            {
                if (fields[j]->type()->getBasicType() == EbtStruct)
                {
                    if (!CompareStructure(*fields[j]->type(),
                                          &rightUnionArray[index],
                                          &leftUnionArray[index]))
                    {
                        return false;
                    }
                }
                else
                {
                    if (leftUnionArray[index] != rightUnionArray[index])
                        return false;
                    index++;
                }
            }
        }
        return true;
    }
    
    bool CompareStructure(const TType &leftNodeType,
                          const TConstantUnion *rightUnionArray,
                          const TConstantUnion *leftUnionArray)
    {
        if (leftNodeType.isArray())
        {
            TType typeWithoutArrayness = leftNodeType;
            typeWithoutArrayness.clearArrayness();
    
            size_t arraySize = leftNodeType.getArraySize();
    
            for (size_t i = 0; i < arraySize; ++i)
            {
                size_t offset = typeWithoutArrayness.getObjectSize() * i;
                if (!CompareStruct(typeWithoutArrayness,
                                   &rightUnionArray[offset],
                                   &leftUnionArray[offset]))
                {
                    return false;
                }
            }
        }
        else
        {
            return CompareStruct(leftNodeType, rightUnionArray, leftUnionArray);
        }
        return true;
    }
    
    TConstantUnion *Vectorize(const TConstantUnion &constant, size_t size)
    {
        TConstantUnion *constUnion = new TConstantUnion[size];
        for (unsigned int i = 0; i < size; ++i)
            constUnion[i] = constant;
    
        return constUnion;
    }
    
    void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType,
                                       TInfoSink &infoSink, TConstantUnion *result)
    {
        std::stringstream constantFoldingErrorStream;
        constantFoldingErrorStream << "'" << GetOperatorString(op)
                                   << "' operation result is undefined for the values passed in";
        infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str());
    
        switch (basicType)
        {
          case EbtFloat :
            result->setFConst(0.0f);
            break;
          case EbtInt:
            result->setIConst(0);
            break;
          case EbtUInt:
            result->setUConst(0u);
            break;
          case EbtBool:
            result->setBConst(false);
            break;
          default:
            break;
        }
    }
    
    float VectorLength(TConstantUnion *paramArray, size_t paramArraySize)
    {
        float result = 0.0f;
        for (size_t i = 0; i < paramArraySize; i++)
        {
            float f = paramArray[i].getFConst();
            result += f * f;
        }
        return sqrtf(result);
    }
    
    float VectorDotProduct(TConstantUnion *paramArray1, TConstantUnion *paramArray2, size_t paramArraySize)
    {
        float result = 0.0f;
        for (size_t i = 0; i < paramArraySize; i++)
            result += paramArray1[i].getFConst() * paramArray2[i].getFConst();
        return result;
    }
    
    TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, const TIntermTyped *originalNode)
    {
        if (constArray == nullptr)
        {
            return nullptr;
        }
        TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType());
        folded->getTypePointer()->setQualifier(EvqConst);
        folded->setLine(originalNode->getLine());
        return folded;
    }
    
    angle::Matrix<float> GetMatrix(TConstantUnion *paramArray, const unsigned int &rows, const unsigned int &cols)
    {
        std::vector<float> elements;
        for (size_t i = 0; i < rows * cols; i++)
            elements.push_back(paramArray[i].getFConst());
        // Transpose is used since the Matrix constructor expects arguments in row-major order,
        // whereas the paramArray is in column-major order.
        return angle::Matrix<float>(elements, rows, cols).transpose();
    }
    
    angle::Matrix<float> GetMatrix(TConstantUnion *paramArray, const unsigned int &size)
    {
        std::vector<float> elements;
        for (size_t i = 0; i < size * size; i++)
            elements.push_back(paramArray[i].getFConst());
        // Transpose is used since the Matrix constructor expects arguments in row-major order,
        // whereas the paramArray is in column-major order.
        return angle::Matrix<float>(elements, size).transpose();
    }
    
    void SetUnionArrayFromMatrix(const angle::Matrix<float> &m, TConstantUnion *resultArray)
    {
        // Transpose is used since the input Matrix is in row-major order,
        // whereas the actual result should be in column-major order.
        angle::Matrix<float> result = m.transpose();
        std::vector<float> resultElements = result.elements();
        for (size_t i = 0; i < resultElements.size(); i++)
            resultArray[i].setFConst(resultElements[i]);
    }
    
    }  // namespace anonymous
    
    
    ////////////////////////////////////////////////////////////////
    //
    // Member functions of the nodes used for building the tree.
    //
    ////////////////////////////////////////////////////////////////
    
    void TIntermTyped::setTypePreservePrecision(const TType &t)
    {
        TPrecision precision = getPrecision();
        mType = t;
        ASSERT(mType.getBasicType() != EbtBool || precision == EbpUndefined);
        mType.setPrecision(precision);
    }
    
    #define REPLACE_IF_IS(node, type, original, replacement) \
        if (node == original) { \
            node = static_cast<type *>(replacement); \
            return true; \
        }
    
    bool TIntermLoop::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        REPLACE_IF_IS(mInit, TIntermNode, original, replacement);
        REPLACE_IF_IS(mCond, TIntermTyped, original, replacement);
        REPLACE_IF_IS(mExpr, TIntermTyped, original, replacement);
        REPLACE_IF_IS(mBody, TIntermNode, original, replacement);
        return false;
    }
    
    bool TIntermBranch::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        REPLACE_IF_IS(mExpression, TIntermTyped, original, replacement);
        return false;
    }
    
    bool TIntermBinary::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        REPLACE_IF_IS(mLeft, TIntermTyped, original, replacement);
        REPLACE_IF_IS(mRight, TIntermTyped, original, replacement);
        return false;
    }
    
    bool TIntermUnary::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        REPLACE_IF_IS(mOperand, TIntermTyped, original, replacement);
        return false;
    }
    
    bool TIntermAggregate::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        for (size_t ii = 0; ii < mSequence.size(); ++ii)
        {
            REPLACE_IF_IS(mSequence[ii], TIntermNode, original, replacement);
        }
        return false;
    }
    
    bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements)
    {
        for (auto it = mSequence.begin(); it < mSequence.end(); ++it)
        {
            if (*it == original)
            {
                it = mSequence.erase(it);
                mSequence.insert(it, replacements.begin(), replacements.end());
                return true;
            }
        }
        return false;
    }
    
    bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions)
    {
        TIntermSequence::size_type itPosition = 0;
        for (auto it = mSequence.begin(); it < mSequence.end(); ++it)
        {
            if (itPosition == position)
            {
                mSequence.insert(it, insertions.begin(), insertions.end());
                return true;
            }
            ++itPosition;
        }
        return false;
    }
    
    void TIntermAggregate::setPrecisionFromChildren()
    {
        mGotPrecisionFromChildren = true;
        if (getBasicType() == EbtBool)
        {
            mType.setPrecision(EbpUndefined);
            return;
        }
    
        TPrecision precision = EbpUndefined;
        TIntermSequence::iterator childIter = mSequence.begin();
        while (childIter != mSequence.end())
        {
            TIntermTyped *typed = (*childIter)->getAsTyped();
            if (typed)
                precision = GetHigherPrecision(typed->getPrecision(), precision);
            ++childIter;
        }
        mType.setPrecision(precision);
    }
    
    void TIntermAggregate::setBuiltInFunctionPrecision()
    {
        // All built-ins returning bool should be handled as ops, not functions.
        ASSERT(getBasicType() != EbtBool);
    
        TPrecision precision = EbpUndefined;
        TIntermSequence::iterator childIter = mSequence.begin();
        while (childIter != mSequence.end())
        {
            TIntermTyped *typed = (*childIter)->getAsTyped();
            // ESSL spec section 8: texture functions get their precision from the sampler.
            if (typed && IsSampler(typed->getBasicType()))
            {
                precision = typed->getPrecision();
                break;
            }
            ++childIter;
        }
        // ESSL 3.0 spec section 8: textureSize always gets highp precision.
        // All other functions that take a sampler are assumed to be texture functions.
        if (mName.getString().find("textureSize") == 0)
            mType.setPrecision(EbpHigh);
        else
            mType.setPrecision(precision);
    }
    
    bool TIntermSelection::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
        REPLACE_IF_IS(mTrueBlock, TIntermNode, original, replacement);
        REPLACE_IF_IS(mFalseBlock, TIntermNode, original, replacement);
        return false;
    }
    
    bool TIntermSwitch::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        REPLACE_IF_IS(mInit, TIntermTyped, original, replacement);
        REPLACE_IF_IS(mStatementList, TIntermAggregate, original, replacement);
        return false;
    }
    
    bool TIntermCase::replaceChildNode(
        TIntermNode *original, TIntermNode *replacement)
    {
        REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
        return false;
    }
    
    TIntermTyped::TIntermTyped(const TIntermTyped &node) : TIntermNode(), mType(node.mType)
    {
        // Copy constructor is disallowed for TIntermNode in order to disallow it for subclasses that
        // don't explicitly allow it, so normal TIntermNode constructor is used to construct the copy.
        // We need to manually copy any fields of TIntermNode besides handling fields in TIntermTyped.
        mLine = node.mLine;
    }
    
    TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node)
    {
        size_t arraySize   = mType.getObjectSize();
        mUnionArrayPointer = new TConstantUnion[arraySize];
        for (size_t i = 0u; i < arraySize; ++i)
        {
            mUnionArrayPointer[i] = node.mUnionArrayPointer[i];
        }
    }
    
    TIntermAggregate::TIntermAggregate(const TIntermAggregate &node)
        : TIntermOperator(node),
          mName(node.mName),
          mUserDefined(node.mUserDefined),
          mFunctionId(node.mFunctionId),
          mUseEmulatedFunction(node.mUseEmulatedFunction),
          mGotPrecisionFromChildren(node.mGotPrecisionFromChildren)
    {
        for (TIntermNode *child : node.mSequence)
        {
            TIntermTyped *typedChild = child->getAsTyped();
            ASSERT(typedChild != nullptr);
            TIntermTyped *childCopy = typedChild->deepCopy();
            mSequence.push_back(childCopy);
        }
    }
    
    TIntermBinary::TIntermBinary(const TIntermBinary &node)
        : TIntermOperator(node), mAddIndexClamp(node.mAddIndexClamp)
    {
        TIntermTyped *leftCopy  = node.mLeft->deepCopy();
        TIntermTyped *rightCopy = node.mRight->deepCopy();
        ASSERT(leftCopy != nullptr && rightCopy != nullptr);
        mLeft  = leftCopy;
        mRight = rightCopy;
    }
    
    TIntermUnary::TIntermUnary(const TIntermUnary &node)
        : TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction)
    {
        TIntermTyped *operandCopy = node.mOperand->deepCopy();
        ASSERT(operandCopy != nullptr);
        mOperand = operandCopy;
    }
    
    TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node)
    {
        // Only supported for ternary nodes, not if statements.
        TIntermTyped *trueTyped  = node.mTrueBlock->getAsTyped();
        TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped();
        ASSERT(trueTyped != nullptr);
        ASSERT(falseTyped != nullptr);
        TIntermTyped *conditionCopy = node.mCondition->deepCopy();
        TIntermTyped *trueCopy      = trueTyped->deepCopy();
        TIntermTyped *falseCopy = falseTyped->deepCopy();
        ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr);
        mCondition  = conditionCopy;
        mTrueBlock  = trueCopy;
        mFalseBlock = falseCopy;
    }
    
    //
    // Say whether or not an operation node changes the value of a variable.
    //
    bool TIntermOperator::isAssignment() const
    {
        switch (mOp)
        {
          case EOpPostIncrement:
          case EOpPostDecrement:
          case EOpPreIncrement:
          case EOpPreDecrement:
          case EOpAssign:
          case EOpAddAssign:
          case EOpSubAssign:
          case EOpMulAssign:
          case EOpVectorTimesMatrixAssign:
          case EOpVectorTimesScalarAssign:
          case EOpMatrixTimesScalarAssign:
          case EOpMatrixTimesMatrixAssign:
          case EOpDivAssign:
          case EOpIModAssign:
          case EOpBitShiftLeftAssign:
          case EOpBitShiftRightAssign:
          case EOpBitwiseAndAssign:
          case EOpBitwiseXorAssign:
          case EOpBitwiseOrAssign:
            return true;
          default:
            return false;
        }
    }
    
    bool TIntermOperator::isMultiplication() const
    {
        switch (mOp)
        {
          case EOpMul:
          case EOpMatrixTimesMatrix:
          case EOpMatrixTimesVector:
          case EOpMatrixTimesScalar:
          case EOpVectorTimesMatrix:
          case EOpVectorTimesScalar:
            return true;
          default:
            return false;
        }
    }
    
    //
    // returns true if the operator is for one of the constructors
    //
    bool TIntermOperator::isConstructor() const
    {
        switch (mOp)
        {
          case EOpConstructVec2:
          case EOpConstructVec3:
          case EOpConstructVec4:
          case EOpConstructMat2:
          case EOpConstructMat2x3:
          case EOpConstructMat2x4:
          case EOpConstructMat3x2:
          case EOpConstructMat3:
          case EOpConstructMat3x4:
          case EOpConstructMat4x2:
          case EOpConstructMat4x3:
          case EOpConstructMat4:
          case EOpConstructFloat:
          case EOpConstructIVec2:
          case EOpConstructIVec3:
          case EOpConstructIVec4:
          case EOpConstructInt:
          case EOpConstructUVec2:
          case EOpConstructUVec3:
          case EOpConstructUVec4:
          case EOpConstructUInt:
          case EOpConstructBVec2:
          case EOpConstructBVec3:
          case EOpConstructBVec4:
          case EOpConstructBool:
          case EOpConstructStruct:
            return true;
          default:
            return false;
        }
    }
    
    //
    // Make sure the type of a unary operator is appropriate for its
    // combination of operation and operand type.
    //
    void TIntermUnary::promote(const TType *funcReturnType)
    {
        switch (mOp)
        {
          case EOpFloatBitsToInt:
          case EOpFloatBitsToUint:
          case EOpIntBitsToFloat:
          case EOpUintBitsToFloat:
          case EOpPackSnorm2x16:
          case EOpPackUnorm2x16:
          case EOpPackHalf2x16:
          case EOpUnpackSnorm2x16:
          case EOpUnpackUnorm2x16:
            mType.setPrecision(EbpHigh);
            break;
          case EOpUnpackHalf2x16:
            mType.setPrecision(EbpMedium);
            break;
          default:
            setType(mOperand->getType());
        }
    
        if (funcReturnType != nullptr)
        {
            if (funcReturnType->getBasicType() == EbtBool)
            {
                // Bool types should not have precision.
                setType(*funcReturnType);
            }
            else
            {
                // Precision of the node has been set based on the operand.
                setTypePreservePrecision(*funcReturnType);
            }
        }
    
        mType.setQualifier(EvqTemporary);
    }
    
    //
    // Establishes the type of the resultant operation, as well as
    // makes the operator the correct one for the operands.
    //
    // For lots of operations it should already be established that the operand
    // combination is valid, but returns false if operator can't work on operands.
    //
    bool TIntermBinary::promote(TInfoSink &infoSink)
    {
        ASSERT(mLeft->isArray() == mRight->isArray());
    
        //
        // Base assumption:  just make the type the same as the left
        // operand.  Then only deviations from this need be coded.
        //
        setType(mLeft->getType());
    
        // The result gets promoted to the highest precision.
        TPrecision higherPrecision = GetHigherPrecision(
            mLeft->getPrecision(), mRight->getPrecision());
        getTypePointer()->setPrecision(higherPrecision);
    
        // Binary operations results in temporary variables unless both
        // operands are const.
        if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst)
        {
            getTypePointer()->setQualifier(EvqTemporary);
        }
    
        const int nominalSize =
            std::max(mLeft->getNominalSize(), mRight->getNominalSize());
    
        //
        // All scalars or structs. Code after this test assumes this case is removed!
        //
        if (nominalSize == 1)
        {
            switch (mOp)
            {
              //
              // Promote to conditional
              //
              case EOpEqual:
              case EOpNotEqual:
              case EOpLessThan:
              case EOpGreaterThan:
              case EOpLessThanEqual:
              case EOpGreaterThanEqual:
                setType(TType(EbtBool, EbpUndefined));
                break;
    
              //
              // And and Or operate on conditionals
              //
              case EOpLogicalAnd:
              case EOpLogicalXor:
              case EOpLogicalOr:
                ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
                setType(TType(EbtBool, EbpUndefined));
                break;
    
              default:
                break;
            }
            return true;
        }
    
        // If we reach here, at least one of the operands is vector or matrix.
        // The other operand could be a scalar, vector, or matrix.
        // Can these two operands be combined?
        //
        TBasicType basicType = mLeft->getBasicType();
        switch (mOp)
        {
          case EOpMul:
            if (!mLeft->isMatrix() && mRight->isMatrix())
            {
                if (mLeft->isVector())
                {
                    mOp = EOpVectorTimesMatrix;
                    setType(TType(basicType, higherPrecision, EvqTemporary,
                                  static_cast<unsigned char>(mRight->getCols()), 1));
                }
                else
                {
                    mOp = EOpMatrixTimesScalar;
                    setType(TType(basicType, higherPrecision, EvqTemporary,
                                  static_cast<unsigned char>(mRight->getCols()), static_cast<unsigned char>(mRight->getRows())));
                }
            }
            else if (mLeft->isMatrix() && !mRight->isMatrix())
            {
                if (mRight->isVector())
                {
                    mOp = EOpMatrixTimesVector;
                    setType(TType(basicType, higherPrecision, EvqTemporary,
                                  static_cast<unsigned char>(mLeft->getRows()), 1));
                }
                else
                {
                    mOp = EOpMatrixTimesScalar;
                }
            }
            else if (mLeft->isMatrix() && mRight->isMatrix())
            {
                mOp = EOpMatrixTimesMatrix;
                setType(TType(basicType, higherPrecision, EvqTemporary,
                              static_cast<unsigned char>(mRight->getCols()), static_cast<unsigned char>(mLeft->getRows())));
            }
            else if (!mLeft->isMatrix() && !mRight->isMatrix())
            {
                if (mLeft->isVector() && mRight->isVector())
                {
                    // leave as component product
                }
                else if (mLeft->isVector() || mRight->isVector())
                {
                    mOp = EOpVectorTimesScalar;
                    setType(TType(basicType, higherPrecision, EvqTemporary,
                                  static_cast<unsigned char>(nominalSize), 1));
                }
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(),
                                      "Missing elses");
                return false;
            }
    
            if (!ValidateMultiplication(mOp, mLeft->getType(), mRight->getType()))
            {
                return false;
            }
            break;
    
          case EOpMulAssign:
            if (!mLeft->isMatrix() && mRight->isMatrix())
            {
                if (mLeft->isVector())
                {
                    mOp = EOpVectorTimesMatrixAssign;
                }
                else
                {
                    return false;
                }
            }
            else if (mLeft->isMatrix() && !mRight->isMatrix())
            {
                if (mRight->isVector())
                {
                    return false;
                }
                else
                {
                    mOp = EOpMatrixTimesScalarAssign;
                }
            }
            else if (mLeft->isMatrix() && mRight->isMatrix())
            {
                mOp = EOpMatrixTimesMatrixAssign;
                setType(TType(basicType, higherPrecision, EvqTemporary,
                              static_cast<unsigned char>(mRight->getCols()), static_cast<unsigned char>(mLeft->getRows())));
            }
            else if (!mLeft->isMatrix() && !mRight->isMatrix())
            {
                if (mLeft->isVector() && mRight->isVector())
                {
                    // leave as component product
                }
                else if (mLeft->isVector() || mRight->isVector())
                {
                    if (!mLeft->isVector())
                        return false;
                    mOp = EOpVectorTimesScalarAssign;
                    setType(TType(basicType, higherPrecision, EvqTemporary,
                                  static_cast<unsigned char>(mLeft->getNominalSize()), 1));
                }
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(),
                                      "Missing elses");
                return false;
            }
    
            if (!ValidateMultiplication(mOp, mLeft->getType(), mRight->getType()))
            {
                return false;
            }
            break;
    
          case EOpAssign:
          case EOpInitialize:
            // No more additional checks are needed.
            ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
                (mLeft->getSecondarySize() == mRight->getSecondarySize()));
            break;
          case EOpAdd:
          case EOpSub:
          case EOpDiv:
          case EOpIMod:
          case EOpBitShiftLeft:
          case EOpBitShiftRight:
          case EOpBitwiseAnd:
          case EOpBitwiseXor:
          case EOpBitwiseOr:
          case EOpAddAssign:
          case EOpSubAssign:
          case EOpDivAssign:
          case EOpIModAssign:
          case EOpBitShiftLeftAssign:
          case EOpBitShiftRightAssign:
          case EOpBitwiseAndAssign:
          case EOpBitwiseXorAssign:
          case EOpBitwiseOrAssign:
            if ((mLeft->isMatrix() && mRight->isVector()) ||
                (mLeft->isVector() && mRight->isMatrix()))
            {
                return false;
            }
    
            // Are the sizes compatible?
            if (mLeft->getNominalSize() != mRight->getNominalSize() ||
                mLeft->getSecondarySize() != mRight->getSecondarySize())
            {
                // If the nominal sizes of operands do not match:
                // One of them must be a scalar.
                if (!mLeft->isScalar() && !mRight->isScalar())
                    return false;
    
                // In the case of compound assignment other than multiply-assign,
                // the right side needs to be a scalar. Otherwise a vector/matrix
                // would be assigned to a scalar. A scalar can't be shifted by a
                // vector either.
                if (!mRight->isScalar() &&
                    (isAssignment() ||
                    mOp == EOpBitShiftLeft ||
                    mOp == EOpBitShiftRight))
                    return false;
            }
    
            {
                const int secondarySize = std::max(
                    mLeft->getSecondarySize(), mRight->getSecondarySize());
                setType(TType(basicType, higherPrecision, EvqTemporary,
                              static_cast<unsigned char>(nominalSize), static_cast<unsigned char>(secondarySize)));
                if (mLeft->isArray())
                {
                    ASSERT(mLeft->getArraySize() == mRight->getArraySize());
                    mType.setArraySize(mLeft->getArraySize());
                }
            }
            break;
    
          case EOpEqual:
          case EOpNotEqual:
          case EOpLessThan:
          case EOpGreaterThan:
          case EOpLessThanEqual:
          case EOpGreaterThanEqual:
            ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
                (mLeft->getSecondarySize() == mRight->getSecondarySize()));
            setType(TType(EbtBool, EbpUndefined));
            break;
    
          default:
            return false;
        }
        return true;
    }
    
    TIntermTyped *TIntermBinary::fold(TInfoSink &infoSink)
    {
        TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion();
        TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion();
        if (leftConstant == nullptr || rightConstant == nullptr)
        {
            return nullptr;
        }
        TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, infoSink);
        return CreateFoldedNode(constArray, this);
    }
    
    TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink)
    {
        TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
        if (operandConstant == nullptr)
        {
            return nullptr;
        }
    
        TConstantUnion *constArray = nullptr;
        switch (mOp)
        {
          case EOpAny:
          case EOpAll:
          case EOpLength:
          case EOpTranspose:
          case EOpDeterminant:
          case EOpInverse:
          case EOpPackSnorm2x16:
          case EOpUnpackSnorm2x16:
          case EOpPackUnorm2x16:
          case EOpUnpackUnorm2x16:
          case EOpPackHalf2x16:
          case EOpUnpackHalf2x16:
            constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink);
            break;
          default:
            constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink);
            break;
        }
        return CreateFoldedNode(constArray, this);
    }
    
    TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
    {
        // Make sure that all params are constant before actual constant folding.
        for (auto *param : *getSequence())
        {
            if (param->getAsConstantUnion() == nullptr)
            {
                return nullptr;
            }
        }
        TConstantUnion *constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink);
        return CreateFoldedNode(constArray, this);
    }
    
    //
    // The fold functions see if an operation on a constant can be done in place,
    // without generating run-time code.
    //
    // Returns the constant value to keep using or nullptr.
    //
    TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink)
    {
        TConstantUnion *leftArray = getUnionArrayPointer();
        TConstantUnion *rightArray = rightNode->getUnionArrayPointer();
    
        if (!leftArray)
            return nullptr;
        if (!rightArray)
            return nullptr;
    
        size_t objectSize = getType().getObjectSize();
    
        // for a case like float f = vec4(2, 3, 4, 5) + 1.2;
        if (rightNode->getType().getObjectSize() == 1 && objectSize > 1)
        {
            rightArray = Vectorize(*rightNode->getUnionArrayPointer(), objectSize);
        }
        else if (rightNode->getType().getObjectSize() > 1 && objectSize == 1)
        {
            // for a case like float f = 1.2 + vec4(2, 3, 4, 5);
            leftArray = Vectorize(*getUnionArrayPointer(), rightNode->getType().getObjectSize());
            objectSize = rightNode->getType().getObjectSize();
        }
    
        TConstantUnion *resultArray = nullptr;
    
        switch(op)
        {
          case EOpAdd:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] + rightArray[i];
            break;
          case EOpSub:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] - rightArray[i];
            break;
    
          case EOpMul:
          case EOpVectorTimesScalar:
          case EOpMatrixTimesScalar:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] * rightArray[i];
            break;
    
          case EOpMatrixTimesMatrix:
            {
                if (getType().getBasicType() != EbtFloat ||
                    rightNode->getBasicType() != EbtFloat)
                {
                    infoSink.info.message(
                        EPrefixInternalError, getLine(),
                        "Constant Folding cannot be done for matrix multiply");
                    return nullptr;
                }
    
                const int leftCols = getCols();
                const int leftRows = getRows();
                const int rightCols = rightNode->getType().getCols();
                const int rightRows = rightNode->getType().getRows();
                const int resultCols = rightCols;
                const int resultRows = leftRows;
    
                resultArray = new TConstantUnion[resultCols * resultRows];
                for (int row = 0; row < resultRows; row++)
                {
                    for (int column = 0; column < resultCols; column++)
                    {
                        resultArray[resultRows * column + row].setFConst(0.0f);
                        for (int i = 0; i < leftCols; i++)
                        {
                            resultArray[resultRows * column + row].setFConst(
                                resultArray[resultRows * column + row].getFConst() +
                                leftArray[i * leftRows + row].getFConst() *
                                rightArray[column * rightRows + i].getFConst());
                        }
                    }
                }
            }
            break;
    
          case EOpDiv:
          case EOpIMod:
            {
                resultArray = new TConstantUnion[objectSize];
                for (size_t i = 0; i < objectSize; i++)
                {
                    switch (getType().getBasicType())
                    {
                      case EbtFloat:
                        if (rightArray[i] == 0.0f)
                        {
                            infoSink.info.message(EPrefixWarning, getLine(),
                                                  "Divide by zero error during constant folding");
                            resultArray[i].setFConst(leftArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX);
                        }
                        else
                        {
                            ASSERT(op == EOpDiv);
                            resultArray[i].setFConst(leftArray[i].getFConst() / rightArray[i].getFConst());
                        }
                        break;
    
                      case EbtInt:
                        if (rightArray[i] == 0)
                        {
                            infoSink.info.message(EPrefixWarning, getLine(),
                                                  "Divide by zero error during constant folding");
                            resultArray[i].setIConst(INT_MAX);
                        }
                        else
                        {
                            if (op == EOpDiv)
                            {
                                resultArray[i].setIConst(leftArray[i].getIConst() / rightArray[i].getIConst());
                            }
                            else
                            {
                                ASSERT(op == EOpIMod);
                                resultArray[i].setIConst(leftArray[i].getIConst() % rightArray[i].getIConst());
                            }
                        }
                        break;
    
                      case EbtUInt:
                        if (rightArray[i] == 0)
                        {
                            infoSink.info.message(EPrefixWarning, getLine(),
                                                  "Divide by zero error during constant folding");
                            resultArray[i].setUConst(UINT_MAX);
                        }
                        else
                        {
                            if (op == EOpDiv)
                            {
                                resultArray[i].setUConst(leftArray[i].getUConst() / rightArray[i].getUConst());
                            }
                            else
                            {
                                ASSERT(op == EOpIMod);
                                resultArray[i].setUConst(leftArray[i].getUConst() % rightArray[i].getUConst());
                            }
                        }
                        break;
    
                      default:
                        infoSink.info.message(EPrefixInternalError, getLine(),
                                              "Constant folding cannot be done for \"/\"");
                        return nullptr;
                    }
                }
            }
            break;
    
          case EOpMatrixTimesVector:
            {
                if (rightNode->getBasicType() != EbtFloat)
                {
                    infoSink.info.message(EPrefixInternalError, getLine(),
                                          "Constant Folding cannot be done for matrix times vector");
                    return nullptr;
                }
    
                const int matrixCols = getCols();
                const int matrixRows = getRows();
    
                resultArray = new TConstantUnion[matrixRows];
    
                for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++)
                {
                    resultArray[matrixRow].setFConst(0.0f);
                    for (int col = 0; col < matrixCols; col++)
                    {
                        resultArray[matrixRow].setFConst(resultArray[matrixRow].getFConst() +
                                                         leftArray[col * matrixRows + matrixRow].getFConst() *
                                                         rightArray[col].getFConst());
                    }
                }
            }
            break;
    
          case EOpVectorTimesMatrix:
            {
                if (getType().getBasicType() != EbtFloat)
                {
                    infoSink.info.message(EPrefixInternalError, getLine(),
                                          "Constant Folding cannot be done for vector times matrix");
                    return nullptr;
                }
    
                const int matrixCols = rightNode->getType().getCols();
                const int matrixRows = rightNode->getType().getRows();
    
                resultArray = new TConstantUnion[matrixCols];
    
                for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++)
                {
                    resultArray[matrixCol].setFConst(0.0f);
                    for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++)
                    {
                        resultArray[matrixCol].setFConst(resultArray[matrixCol].getFConst() +
                                                         leftArray[matrixRow].getFConst() *
                                                         rightArray[matrixCol * matrixRows + matrixRow].getFConst());
                    }
                }
            }
            break;
    
          case EOpLogicalAnd:
            {
                resultArray = new TConstantUnion[objectSize];
                for (size_t i = 0; i < objectSize; i++)
                {
                    resultArray[i] = leftArray[i] && rightArray[i];
                }
            }
            break;
    
          case EOpLogicalOr:
            {
                resultArray = new TConstantUnion[objectSize];
                for (size_t i = 0; i < objectSize; i++)
                {
                    resultArray[i] = leftArray[i] || rightArray[i];
                }
            }
            break;
    
          case EOpLogicalXor:
            {
                resultArray = new TConstantUnion[objectSize];
                for (size_t i = 0; i < objectSize; i++)
                {
                    switch (getType().getBasicType())
                    {
                      case EbtBool:
                        resultArray[i].setBConst(leftArray[i] != rightArray[i]);
                        break;
                      default:
                        UNREACHABLE();
                        break;
                    }
                }
            }
            break;
    
          case EOpBitwiseAnd:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] & rightArray[i];
            break;
          case EOpBitwiseXor:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] ^ rightArray[i];
            break;
          case EOpBitwiseOr:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] | rightArray[i];
            break;
          case EOpBitShiftLeft:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] << rightArray[i];
            break;
          case EOpBitShiftRight:
            resultArray = new TConstantUnion[objectSize];
            for (size_t i = 0; i < objectSize; i++)
                resultArray[i] = leftArray[i] >> rightArray[i];
            break;
    
          case EOpLessThan:
            ASSERT(objectSize == 1);
            resultArray = new TConstantUnion[1];
            resultArray->setBConst(*leftArray < *rightArray);
            break;
    
          case EOpGreaterThan:
            ASSERT(objectSize == 1);
            resultArray = new TConstantUnion[1];
            resultArray->setBConst(*leftArray > *rightArray);
            break;
    
          case EOpLessThanEqual:
            ASSERT(objectSize == 1);
            resultArray = new TConstantUnion[1];
            resultArray->setBConst(!(*leftArray > *rightArray));
            break;
    
          case EOpGreaterThanEqual:
            ASSERT(objectSize == 1);
            resultArray = new TConstantUnion[1];
            resultArray->setBConst(!(*leftArray < *rightArray));
            break;
    
          case EOpEqual:
          case EOpNotEqual:
            {
                resultArray = new TConstantUnion[1];
                bool equal = true;
                if (getType().getBasicType() == EbtStruct)
                {
                    equal = CompareStructure(getType(), rightArray, leftArray);
                }
                else
                {
                    for (size_t i = 0; i < objectSize; i++)
                    {
                        if (leftArray[i] != rightArray[i])
                        {
                            equal = false;
                            break;  // break out of for loop
                        }
                    }
                }
                if (op == EOpEqual)
                {
                    resultArray->setBConst(equal);
                }
                else
                {
                    resultArray->setBConst(!equal);
                }
            }
            break;
    
          default:
            infoSink.info.message(
                EPrefixInternalError, getLine(),
                "Invalid operator for constant folding");
            return nullptr;
        }
        return resultArray;
    }
    
    //
    // The fold functions see if an operation on a constant can be done in place,
    // without generating run-time code.
    //
    // Returns the constant value to keep using or nullptr.
    //
    TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink)
    {
        //
        // Do operations where the return type has a different number of components compared to the operand type.
        //
    
        TConstantUnion *operandArray = getUnionArrayPointer();
        if (!operandArray)
            return nullptr;
    
        size_t objectSize = getType().getObjectSize();
        TConstantUnion *resultArray = nullptr;
        switch (op)
        {
          case EOpAny:
            if (getType().getBasicType() == EbtBool)
            {
                resultArray = new TConstantUnion();
                resultArray->setBConst(false);
                for (size_t i = 0; i < objectSize; i++)
                {
                    if (operandArray[i].getBConst())
                    {
                        resultArray->setBConst(true);
                        break;
                    }
                }
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpAll:
            if (getType().getBasicType() == EbtBool)
            {
                resultArray = new TConstantUnion();
                resultArray->setBConst(true);
                for (size_t i = 0; i < objectSize; i++)
                {
                    if (!operandArray[i].getBConst())
                    {
                        resultArray->setBConst(false);
                        break;
                    }
                }
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpLength:
            if (getType().getBasicType() == EbtFloat)
            {
                resultArray = new TConstantUnion();
                resultArray->setFConst(VectorLength(operandArray, objectSize));
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpTranspose:
            if (getType().getBasicType() == EbtFloat)
            {
                resultArray = new TConstantUnion[objectSize];
                angle::Matrix<float> result =
                    GetMatrix(operandArray, getType().getNominalSize(), getType().getSecondarySize()).transpose();
                SetUnionArrayFromMatrix(result, resultArray);
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpDeterminant:
            if (getType().getBasicType() == EbtFloat)
            {
                unsigned int size = getType().getNominalSize();
                ASSERT(size >= 2 && size <= 4);
                resultArray = new TConstantUnion();
                resultArray->setFConst(GetMatrix(operandArray, size).determinant());
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpInverse:
            if (getType().getBasicType() == EbtFloat)
            {
                unsigned int size = getType().getNominalSize();
                ASSERT(size >= 2 && size <= 4);
                resultArray = new TConstantUnion[objectSize];
                angle::Matrix<float> result = GetMatrix(operandArray, size).inverse();
                SetUnionArrayFromMatrix(result, resultArray);
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpPackSnorm2x16:
            if (getType().getBasicType() == EbtFloat)
            {
                ASSERT(getType().getNominalSize() == 2);
                resultArray = new TConstantUnion();
                resultArray->setUConst(gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpUnpackSnorm2x16:
            if (getType().getBasicType() == EbtUInt)
            {
                resultArray = new TConstantUnion[2];
                float f1, f2;
                gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2);
                resultArray[0].setFConst(f1);
                resultArray[1].setFConst(f2);
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpPackUnorm2x16:
            if (getType().getBasicType() == EbtFloat)
            {
                ASSERT(getType().getNominalSize() == 2);
                resultArray = new TConstantUnion();
                resultArray->setUConst(gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpUnpackUnorm2x16:
            if (getType().getBasicType() == EbtUInt)
            {
                resultArray = new TConstantUnion[2];
                float f1, f2;
                gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2);
                resultArray[0].setFConst(f1);
                resultArray[1].setFConst(f2);
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpPackHalf2x16:
            if (getType().getBasicType() == EbtFloat)
            {
                ASSERT(getType().getNominalSize() == 2);
                resultArray = new TConstantUnion();
                resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
    
          case EOpUnpackHalf2x16:
            if (getType().getBasicType() == EbtUInt)
            {
                resultArray = new TConstantUnion[2];
                float f1, f2;
                gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2);
                resultArray[0].setFConst(f1);
                resultArray[1].setFConst(f2);
                break;
            }
            else
            {
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
            }
            break;
    
          default:
            break;
        }
    
        return resultArray;
    }
    
    TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink)
    {
        //
        // Do unary operations where the return type is the same as operand type.
        //
    
        TConstantUnion *operandArray = getUnionArrayPointer();
        if (!operandArray)
            return nullptr;
    
        size_t objectSize = getType().getObjectSize();
    
        TConstantUnion *resultArray = new TConstantUnion[objectSize];
        for (size_t i = 0; i < objectSize; i++)
        {
            switch(op)
            {
              case EOpNegative:
                switch (getType().getBasicType())
                {
                  case EbtFloat:
                    resultArray[i].setFConst(-operandArray[i].getFConst());
                    break;
                  case EbtInt:
                    resultArray[i].setIConst(-operandArray[i].getIConst());
                    break;
                  case EbtUInt:
                    resultArray[i].setUConst(static_cast<unsigned int>(
                        -static_cast<int>(operandArray[i].getUConst())));
                    break;
                  default:
                    infoSink.info.message(
                        EPrefixInternalError, getLine(),
                        "Unary operation not folded into constant");
                    return nullptr;
                }
                break;
    
              case EOpPositive:
                switch (getType().getBasicType())
                {
                  case EbtFloat:
                    resultArray[i].setFConst(operandArray[i].getFConst());
                    break;
                  case EbtInt:
                    resultArray[i].setIConst(operandArray[i].getIConst());
                    break;
                  case EbtUInt:
                    resultArray[i].setUConst(static_cast<unsigned int>(
                        static_cast<int>(operandArray[i].getUConst())));
                    break;
                  default:
                    infoSink.info.message(
                        EPrefixInternalError, getLine(),
                        "Unary operation not folded into constant");
                    return nullptr;
                }
                break;
    
              case EOpLogicalNot:
                // this code is written for possible future use,
                // will not get executed currently
                switch (getType().getBasicType())
                {
                  case EbtBool:
                    resultArray[i].setBConst(!operandArray[i].getBConst());
                    break;
                  default:
                    infoSink.info.message(
                        EPrefixInternalError, getLine(),
                        "Unary operation not folded into constant");
                    return nullptr;
                }
                break;
    
              case EOpBitwiseNot:
                switch (getType().getBasicType())
                {
                  case EbtInt:
                    resultArray[i].setIConst(~operandArray[i].getIConst());
                    break;
                  case EbtUInt:
                    resultArray[i].setUConst(~operandArray[i].getUConst());
                    break;
                  default:
                    infoSink.info.message(
                        EPrefixInternalError, getLine(),
                        "Unary operation not folded into constant");
                    return nullptr;
                }
                break;
    
              case EOpRadians:
                if (getType().getBasicType() == EbtFloat)
                {
                    resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst());
                    break;
                }
                infoSink.info.message(
                    EPrefixInternalError, getLine(),
                    "Unary operation not folded into constant");
                return nullptr;
    
              case EOpDegrees:
                if (getType().getBasicType() == EbtFloat)
                {
                    resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst());
                    break;
                }
                infoSink.info.message(
                    EPrefixInternalError, getLine(),
                    "Unary operation not folded into constant");
                return nullptr;
    
              case EOpSin:
                if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i]))
                   return nullptr;
                break;
    
              case EOpCos:
                if (!foldFloatTypeUnary(operandArray[i], &cosf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpTan:
                if (!foldFloatTypeUnary(operandArray[i], &tanf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpAsin:
                // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0.
                if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &asinf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpAcos:
                // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0.
                if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &acosf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpAtan:
                if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpSinh:
                if (!foldFloatTypeUnary(operandArray[i], &sinhf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpCosh:
                if (!foldFloatTypeUnary(operandArray[i], &coshf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpTanh:
                if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpAsinh:
                if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpAcosh:
                // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
                if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 1.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &acoshf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpAtanh:
                // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0.
                if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) >= 1.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &atanhf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpAbs:
                switch (getType().getBasicType())
                {
                  case EbtFloat:
                    resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
                    break;
                  case EbtInt:
                    resultArray[i].setIConst(abs(operandArray[i].getIConst()));
                    break;
                  default:
                    infoSink.info.message(
                        EPrefixInternalError, getLine(),
                        "Unary operation not folded into constant");
                    return nullptr;
                }
                break;
    
              case EOpSign:
                switch (getType().getBasicType())
                {
                  case EbtFloat:
                    {
                        float fConst = operandArray[i].getFConst();
                        float fResult = 0.0f;
                        if (fConst > 0.0f)
                            fResult = 1.0f;
                        else if (fConst < 0.0f)
                            fResult = -1.0f;
                        resultArray[i].setFConst(fResult);
                    }
                    break;
                  case EbtInt:
                    {
                        int iConst = operandArray[i].getIConst();
                        int iResult = 0;
                        if (iConst > 0)
                            iResult = 1;
                        else if (iConst < 0)
                            iResult = -1;
                        resultArray[i].setIConst(iResult);
                    }
                    break;
                  default:
                    infoSink.info.message(
                        EPrefixInternalError, getLine(),
                        "Unary operation not folded into constant");
                    return nullptr;
                }
                break;
    
              case EOpFloor:
                if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpTrunc:
                if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpRound:
                if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpRoundEven:
                if (getType().getBasicType() == EbtFloat)
                {
                    float x = operandArray[i].getFConst();
                    float result;
                    float fractPart = modff(x, &result);
                    if (fabsf(fractPart) == 0.5f)
                        result = 2.0f * roundf(x / 2.0f);
                    else
                        result = roundf(x);
                    resultArray[i].setFConst(result);
                    break;
                }
                infoSink.info.message(
                    EPrefixInternalError, getLine(),
                    "Unary operation not folded into constant");
                return nullptr;
    
              case EOpCeil:
                if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpFract:
                if (getType().getBasicType() == EbtFloat)
                {
                    float x = operandArray[i].getFConst();
                    resultArray[i].setFConst(x - floorf(x));
                    break;
                }
                infoSink.info.message(
                    EPrefixInternalError, getLine(),
                    "Unary operation not folded into constant");
                return nullptr;
    
              case EOpIsNan:
                if (getType().getBasicType() == EbtFloat)
                {
                    resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst()));
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              case EOpIsInf:
                if (getType().getBasicType() == EbtFloat)
                {
                    resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst()));
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              case EOpFloatBitsToInt:
                if (getType().getBasicType() == EbtFloat)
                {
                    resultArray[i].setIConst(gl::bitCast<int32_t>(operandArray[0].getFConst()));
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              case EOpFloatBitsToUint:
                if (getType().getBasicType() == EbtFloat)
                {
                    resultArray[i].setUConst(gl::bitCast<uint32_t>(operandArray[0].getFConst()));
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              case EOpIntBitsToFloat:
                if (getType().getBasicType() == EbtInt)
                {
                    resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getIConst()));
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              case EOpUintBitsToFloat:
                if (getType().getBasicType() == EbtUInt)
                {
                    resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getUConst()));
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              case EOpExp:
                if (!foldFloatTypeUnary(operandArray[i], &expf, infoSink, &resultArray[i]))
                  return nullptr;
                break;
    
              case EOpLog:
                // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
                if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpExp2:
                if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpLog2:
                // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
                // And log2f is not available on some plarforms like old android, so just using log(x)/log(2) here.
                if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
                    return nullptr;
                else
                    resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
                break;
    
              case EOpSqrt:
                // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
                if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 0.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
                    return nullptr;
                break;
    
              case EOpInverseSqrt:
                // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
                // so getting the square root first using builtin function sqrt() and then taking its inverse.
                // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set result to 0.
                if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
                else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
                    return nullptr;
                else
                    resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
                break;
    
              case EOpVectorLogicalNot:
                if (getType().getBasicType() == EbtBool)
                {
                    resultArray[i].setBConst(!operandArray[i].getBConst());
                    break;
                }
                infoSink.info.message(
                    EPrefixInternalError, getLine(),
                    "Unary operation not folded into constant");
                return nullptr;
    
              case EOpNormalize:
                if (getType().getBasicType() == EbtFloat)
                {
                    float x = operandArray[i].getFConst();
                    float length = VectorLength(operandArray, objectSize);
                    if (length)
                        resultArray[i].setFConst(x / length);
                    else
                        UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink,
                                                      &resultArray[i]);
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              case EOpDFdx:
              case EOpDFdy:
              case EOpFwidth:
                if (getType().getBasicType() == EbtFloat)
                {
                    // Derivatives of constant arguments should be 0.
                    resultArray[i].setFConst(0.0f);
                    break;
                }
                infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
                return nullptr;
    
              default:
                return nullptr;
            }
        }
    
        return resultArray;
    }
    
    bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc,
                                                  TInfoSink &infoSink, TConstantUnion *result) const
    {
        ASSERT(builtinFunc);
    
        if (getType().getBasicType() == EbtFloat)
        {
            result->setFConst(builtinFunc(parameter.getFConst()));
            return true;
        }
    
        infoSink.info.message(
            EPrefixInternalError, getLine(),
            "Unary operation not folded into constant");
        return false;
    }
    
    // static
    TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink)
    {
        TOperator op = aggregate->getOp();
        TIntermSequence *sequence = aggregate->getSequence();
        unsigned int paramsCount = static_cast<unsigned int>(sequence->size());
        std::vector<TConstantUnion *> unionArrays(paramsCount);
        std::vector<size_t> objectSizes(paramsCount);
        size_t maxObjectSize = 0;
        TBasicType basicType = EbtVoid;
        TSourceLoc loc;
        for (unsigned int i = 0; i < paramsCount; i++)
        {
            TIntermConstantUnion *paramConstant = (*sequence)[i]->getAsConstantUnion();
            ASSERT(paramConstant != nullptr); // Should be checked already.
    
            if (i == 0)
            {
                basicType = paramConstant->getType().getBasicType();
                loc = paramConstant->getLine();
            }
            unionArrays[i] = paramConstant->getUnionArrayPointer();
            objectSizes[i] = paramConstant->getType().getObjectSize();
            if (objectSizes[i] > maxObjectSize)
                maxObjectSize = objectSizes[i];
        }
    
        if (!(*sequence)[0]->getAsTyped()->isMatrix())
        {
            for (unsigned int i = 0; i < paramsCount; i++)
                if (objectSizes[i] != maxObjectSize)
                    unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize);
        }
    
        TConstantUnion *resultArray = nullptr;
        if (paramsCount == 2)
        {
            //
            // Binary built-in
            //
            switch (op)
            {
              case EOpAtan:
                {
                    if (basicType == EbtFloat)
                    {
                        resultArray = new TConstantUnion[maxObjectSize];
                        for (size_t i = 0; i < maxObjectSize; i++)
                        {
                            float y = unionArrays[0][i].getFConst();
                            float x = unionArrays[1][i].getFConst();
                            // Results are undefined if x and y are both 0.
                            if (x == 0.0f && y == 0.0f)
                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                            else
                                resultArray[i].setFConst(atan2f(y, x));
                        }
                    }
                    else
                        UNREACHABLE();
                }
                break;
    
              case EOpPow:
                {
                    if (basicType == EbtFloat)
                    {
                        resultArray = new TConstantUnion[maxObjectSize];
                        for (size_t i = 0; i < maxObjectSize; i++)
                        {
                            float x = unionArrays[0][i].getFConst();
                            float y = unionArrays[1][i].getFConst();
                            // Results are undefined if x < 0.
                            // Results are undefined if x = 0 and y <= 0.
                            if (x < 0.0f)
                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                            else if (x == 0.0f && y <= 0.0f)
                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                            else
                                resultArray[i].setFConst(powf(x, y));
                        }
                    }
                    else
                        UNREACHABLE();
                }
                break;
    
              case EOpMod:
                {
                    if (basicType == EbtFloat)
                    {
                        resultArray = new TConstantUnion[maxObjectSize];
                        for (size_t i = 0; i < maxObjectSize; i++)
                        {
                            float x = unionArrays[0][i].getFConst();
                            float y = unionArrays[1][i].getFConst();
                            resultArray[i].setFConst(x - y * floorf(x / y));
                        }
                    }
                    else
                        UNREACHABLE();
                }
                break;
    
              case EOpMin:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
                            break;
                          case EbtInt:
                            resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
                            break;
                          case EbtUInt:
                            resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpMax:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
                            break;
                          case EbtInt:
                            resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
                            break;
                          case EbtUInt:
                            resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpStep:
                {
                    if (basicType == EbtFloat)
                    {
                        resultArray = new TConstantUnion[maxObjectSize];
                        for (size_t i = 0; i < maxObjectSize; i++)
                            resultArray[i].setFConst(unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f);
                    }
                    else
                        UNREACHABLE();
                }
                break;
    
              case EOpLessThan:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setBConst(unionArrays[0][i].getFConst() < unionArrays[1][i].getFConst());
                            break;
                          case EbtInt:
                            resultArray[i].setBConst(unionArrays[0][i].getIConst() < unionArrays[1][i].getIConst());
                            break;
                          case EbtUInt:
                            resultArray[i].setBConst(unionArrays[0][i].getUConst() < unionArrays[1][i].getUConst());
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpLessThanEqual:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setBConst(unionArrays[0][i].getFConst() <= unionArrays[1][i].getFConst());
                            break;
                          case EbtInt:
                            resultArray[i].setBConst(unionArrays[0][i].getIConst() <= unionArrays[1][i].getIConst());
                            break;
                          case EbtUInt:
                            resultArray[i].setBConst(unionArrays[0][i].getUConst() <= unionArrays[1][i].getUConst());
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpGreaterThan:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setBConst(unionArrays[0][i].getFConst() > unionArrays[1][i].getFConst());
                            break;
                          case EbtInt:
                            resultArray[i].setBConst(unionArrays[0][i].getIConst() > unionArrays[1][i].getIConst());
                            break;
                          case EbtUInt:
                            resultArray[i].setBConst(unionArrays[0][i].getUConst() > unionArrays[1][i].getUConst());
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpGreaterThanEqual:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setBConst(unionArrays[0][i].getFConst() >= unionArrays[1][i].getFConst());
                            break;
                          case EbtInt:
                            resultArray[i].setBConst(unionArrays[0][i].getIConst() >= unionArrays[1][i].getIConst());
                            break;
                          case EbtUInt:
                            resultArray[i].setBConst(unionArrays[0][i].getUConst() >= unionArrays[1][i].getUConst());
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpVectorEqual:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setBConst(unionArrays[0][i].getFConst() == unionArrays[1][i].getFConst());
                            break;
                          case EbtInt:
                            resultArray[i].setBConst(unionArrays[0][i].getIConst() == unionArrays[1][i].getIConst());
                            break;
                          case EbtUInt:
                            resultArray[i].setBConst(unionArrays[0][i].getUConst() == unionArrays[1][i].getUConst());
                            break;
                          case EbtBool:
                            resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst());
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpVectorNotEqual:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            resultArray[i].setBConst(unionArrays[0][i].getFConst() != unionArrays[1][i].getFConst());
                            break;
                          case EbtInt:
                            resultArray[i].setBConst(unionArrays[0][i].getIConst() != unionArrays[1][i].getIConst());
                            break;
                          case EbtUInt:
                            resultArray[i].setBConst(unionArrays[0][i].getUConst() != unionArrays[1][i].getUConst());
                            break;
                          case EbtBool:
                            resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst());
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpDistance:
                if (basicType == EbtFloat)
                {
                    TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize];
                    resultArray = new TConstantUnion();
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        float x = unionArrays[0][i].getFConst();
                        float y = unionArrays[1][i].getFConst();
                        distanceArray[i].setFConst(x - y);
                    }
                    resultArray->setFConst(VectorLength(distanceArray, maxObjectSize));
                }
                else
                    UNREACHABLE();
                break;
    
              case EOpDot:
    
                if (basicType == EbtFloat)
                {
                    resultArray = new TConstantUnion();
                    resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
                }
                else
                    UNREACHABLE();
                break;
    
              case EOpCross:
                if (basicType == EbtFloat && maxObjectSize == 3)
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    float x0 = unionArrays[0][0].getFConst();
                    float x1 = unionArrays[0][1].getFConst();
                    float x2 = unionArrays[0][2].getFConst();
                    float y0 = unionArrays[1][0].getFConst();
                    float y1 = unionArrays[1][1].getFConst();
                    float y2 = unionArrays[1][2].getFConst();
                    resultArray[0].setFConst(x1 * y2 - y1 * x2);
                    resultArray[1].setFConst(x2 * y0 - y2 * x0);
                    resultArray[2].setFConst(x0 * y1 - y0 * x1);
                }
                else
                    UNREACHABLE();
                break;
    
              case EOpReflect:
                if (basicType == EbtFloat)
                {
                    // genType reflect (genType I, genType N) :
                    //     For the incident vector I and surface orientation N, returns the reflection direction:
                    //     I - 2 * dot(N, I) * N.
                    resultArray = new TConstantUnion[maxObjectSize];
                    float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        float result = unionArrays[0][i].getFConst() -
                                       2.0f * dotProduct * unionArrays[1][i].getFConst();
                        resultArray[i].setFConst(result);
                    }
                }
                else
                    UNREACHABLE();
                break;
    
              case EOpMul:
                if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
                    (*sequence)[1]->getAsTyped()->isMatrix())
                {
                    // Perform component-wise matrix multiplication.
                    resultArray = new TConstantUnion[maxObjectSize];
                    int size = (*sequence)[0]->getAsTyped()->getNominalSize();
                    angle::Matrix<float> result =
                        GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
                    SetUnionArrayFromMatrix(result, resultArray);
                }
                else
                    UNREACHABLE();
                break;
    
              case EOpOuterProduct:
                if (basicType == EbtFloat)
                {
                    size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize();
                    size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize();
                    resultArray = new TConstantUnion[numRows * numCols];
                    angle::Matrix<float> result =
                        GetMatrix(unionArrays[0], 1, static_cast<int>(numCols))
                            .outerProduct(GetMatrix(unionArrays[1], static_cast<int>(numRows), 1));
                    SetUnionArrayFromMatrix(result, resultArray);
                }
                else
                    UNREACHABLE();
                break;
    
              default:
                UNREACHABLE();
                // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above.
                return nullptr;
            }
        }
        else if (paramsCount == 3)
        {
            //
            // Ternary built-in
            //
            switch (op)
            {
              case EOpClamp:
                {
                    resultArray = new TConstantUnion[maxObjectSize];
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        switch (basicType)
                        {
                          case EbtFloat:
                            {
                                float x = unionArrays[0][i].getFConst();
                                float min = unionArrays[1][i].getFConst();
                                float max = unionArrays[2][i].getFConst();
                                // Results are undefined if min > max.
                                if (min > max)
                                    UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                                else
                                    resultArray[i].setFConst(gl::clamp(x, min, max));
                            }
                            break;
                          case EbtInt:
                            {
                                int x = unionArrays[0][i].getIConst();
                                int min = unionArrays[1][i].getIConst();
                                int max = unionArrays[2][i].getIConst();
                                // Results are undefined if min > max.
                                if (min > max)
                                    UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                                else
                                    resultArray[i].setIConst(gl::clamp(x, min, max));
                            }
                            break;
                          case EbtUInt:
                            {
                                unsigned int x = unionArrays[0][i].getUConst();
                                unsigned int min = unionArrays[1][i].getUConst();
                                unsigned int max = unionArrays[2][i].getUConst();
                                // Results are undefined if min > max.
                                if (min > max)
                                    UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                                else
                                    resultArray[i].setUConst(gl::clamp(x, min, max));
                            }
                            break;
                          default:
                            UNREACHABLE();
                            break;
                        }
                    }
                }
                break;
    
              case EOpMix:
                {
                    if (basicType == EbtFloat)
                    {
                        resultArray = new TConstantUnion[maxObjectSize];
                        for (size_t i = 0; i < maxObjectSize; i++)
                        {
                            float x = unionArrays[0][i].getFConst();
                            float y = unionArrays[1][i].getFConst();
                            TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
                            if (type == EbtFloat)
                            {
                                // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
                                float a = unionArrays[2][i].getFConst();
                                resultArray[i].setFConst(x * (1.0f - a) + y * a);
                            }
                            else // 3rd parameter is EbtBool
                            {
                                ASSERT(type == EbtBool);
                                // Selects which vector each returned component comes from.
                                // For a component of a that is false, the corresponding component of x is returned.
                                // For a component of a that is true, the corresponding component of y is returned.
                                bool a = unionArrays[2][i].getBConst();
                                resultArray[i].setFConst(a ? y : x);
                            }
                        }
                    }
                    else
                        UNREACHABLE();
                }
                break;
    
              case EOpSmoothStep:
                {
                    if (basicType == EbtFloat)
                    {
                        resultArray = new TConstantUnion[maxObjectSize];
                        for (size_t i = 0; i < maxObjectSize; i++)
                        {
                            float edge0 = unionArrays[0][i].getFConst();
                            float edge1 = unionArrays[1][i].getFConst();
                            float x = unionArrays[2][i].getFConst();
                            // Results are undefined if edge0 >= edge1.
                            if (edge0 >= edge1)
                            {
                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
                            }
                            else
                            {
                                // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
                                // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
                                float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
                                resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
                            }
                        }
                    }
                    else
                        UNREACHABLE();
                }
                break;
    
              case EOpFaceForward:
                if (basicType == EbtFloat)
                {
                    // genType faceforward(genType N, genType I, genType Nref) :
                    //     If dot(Nref, I) < 0 return N, otherwise return -N.
                    resultArray = new TConstantUnion[maxObjectSize];
                    float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize);
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        if (dotProduct < 0)
                            resultArray[i].setFConst(unionArrays[0][i].getFConst());
                        else
                            resultArray[i].setFConst(-unionArrays[0][i].getFConst());
                    }
                }
                else
                    UNREACHABLE();
                break;
    
              case EOpRefract:
                if (basicType == EbtFloat)
                {
                    // genType refract(genType I, genType N, float eta) :
                    //     For the incident vector I and surface normal N, and the ratio of indices of refraction eta,
                    //     return the refraction vector. The result is computed by
                    //         k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
                    //         if (k < 0.0)
                    //             return genType(0.0)
                    //         else
                    //             return eta * I - (eta * dot(N, I) + sqrt(k)) * N
                    resultArray = new TConstantUnion[maxObjectSize];
                    float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
                    for (size_t i = 0; i < maxObjectSize; i++)
                    {
                        float eta = unionArrays[2][i].getFConst();
                        float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
                        if (k < 0.0f)
                            resultArray[i].setFConst(0.0f);
                        else
                            resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() -
                                                        (eta * dotProduct + sqrtf(k)) * unionArrays[1][i].getFConst());
                    }
                }
                else
                    UNREACHABLE();
                break;
    
              default:
                UNREACHABLE();
                // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above.
                return nullptr;
            }
        }
        return resultArray;
    }
    
    // static
    TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunction)
    {
        if (hashFunction == NULL || name.empty())
            return name;
        khronos_uint64_t number = (*hashFunction)(name.c_str(), name.length());
        TStringStream stream;
        stream << HASHED_NAME_PREFIX << std::hex << number;
        TString hashedName = stream.str();
        return hashedName;
    }
    
    void TIntermTraverser::updateTree()
    {
        for (size_t ii = 0; ii < mInsertions.size(); ++ii)
        {
            const NodeInsertMultipleEntry &insertion = mInsertions[ii];
            ASSERT(insertion.parent);
            bool inserted = insertion.parent->insertChildNodes(insertion.position, insertion.insertions);
            ASSERT(inserted);
            UNUSED_ASSERTION_VARIABLE(inserted);
        }
        for (size_t ii = 0; ii < mReplacements.size(); ++ii)
        {
            const NodeUpdateEntry &replacement = mReplacements[ii];
            ASSERT(replacement.parent);
            bool replaced = replacement.parent->replaceChildNode(
                replacement.original, replacement.replacement);
            ASSERT(replaced);
            UNUSED_ASSERTION_VARIABLE(replaced);
    
            if (!replacement.originalBecomesChildOfReplacement)
            {
                // In AST traversing, a parent is visited before its children.
                // After we replace a node, if its immediate child is to
                // be replaced, we need to make sure we don't update the replaced
                // node; instead, we update the replacement node.
                for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj)
                {
                    NodeUpdateEntry &replacement2 = mReplacements[jj];
                    if (replacement2.parent == replacement.original)
                        replacement2.parent = replacement.replacement;
                }
            }
        }
        for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii)
        {
            const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii];
            ASSERT(replacement.parent);
            bool replaced = replacement.parent->replaceChildNodeWithMultiple(
                replacement.original, replacement.replacements);
            ASSERT(replaced);
            UNUSED_ASSERTION_VARIABLE(replaced);
        }
    
        mInsertions.clear();
        mReplacements.clear();
        mMultiReplacements.clear();
    }