Edit

kc3-lang/angle/src/compiler/Intermediate.cpp

Branch :

  • Show log

    Commit

  • Author : alokp@chromium.org
    Date : 2010-08-23 21:01:13
    Hash : 32cfaf4b
    Message : TIntermBinary::promote() was incorrectly marking the type of result as const in some cases. The result can only be const if both operands are const. Also cleaned up the function to remove redundant/repeated checks. BUG=24 TEST=OpenGL ES 2.0 Conformance tests Review URL: http://codereview.appspot.com/1938047 git-svn-id: https://angleproject.googlecode.com/svn/trunk@385 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/compiler/Intermediate.cpp
  • //
    // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    //
    // Build the intermediate representation.
    //
    
    #include <float.h>
    #include <limits.h>
    #include <algorithm>
    
    #include "compiler/localintermediate.h"
    #include "compiler/QualifierAlive.h"
    #include "compiler/RemoveTree.h"
    
    bool CompareStructure(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray);
    
    static TPrecision GetHigherPrecision( TPrecision left, TPrecision right ){
        return left > right ? left : right;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    //
    // First set of functions are to help build the intermediate representation.
    // These functions are not member functions of the nodes.
    // They are called from parser productions.
    //
    /////////////////////////////////////////////////////////////////////////////
    
    //
    // Add a terminal node for an identifier in an expression.
    //
    // Returns the added node.
    //
    TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, TSourceLoc line)
    {
        TIntermSymbol* node = new TIntermSymbol(id, name, type);
        node->setLine(line);
    
        return node;
    }
    
    //
    // Connect two nodes with a new parent that does a binary operation on the nodes.
    //
    // Returns the added node.
    //
    TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line, TSymbolTable& symbolTable)
    {
        switch (op) {
            case EOpLessThan:
            case EOpGreaterThan:
            case EOpLessThanEqual:
            case EOpGreaterThanEqual:
                if (left->getType().isMatrix() || left->getType().isArray() || left->getType().isVector() || left->getType().getBasicType() == EbtStruct) {
                    return 0;
                }
                break;
            case EOpLogicalOr:
            case EOpLogicalXor:
            case EOpLogicalAnd:
                if (left->getType().getBasicType() != EbtBool || left->getType().isMatrix() || left->getType().isArray() || left->getType().isVector()) {
                    return 0;
                }
                break;
            case EOpAdd:
            case EOpSub:
            case EOpDiv:
            case EOpMul:
                if (left->getType().getBasicType() == EbtStruct || left->getType().getBasicType() == EbtBool)
                    return 0;
            default: break;
        }
    
        //
        // First try converting the children to compatible types.
        //
    
        if (!(left->getType().getStruct() && right->getType().getStruct())) {
            TIntermTyped* child = addConversion(op, left->getType(), right);
            if (child)
                right = child;
            else {
                child = addConversion(op, right->getType(), left);
                if (child)
                    left = child;
                else
                    return 0;
            }
        } else {
            if (left->getType() != right->getType())
                return 0;
        }
    
    
        //
        // Need a new node holding things together then.  Make
        // one and promote it to the right type.
        //
        TIntermBinary* node = new TIntermBinary(op);
        if (line == 0)
            line = right->getLine();
        node->setLine(line);
    
        node->setLeft(left);
        node->setRight(right);
        if (! node->promote(infoSink))
            return 0;
    
        TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion();
        TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion();
    
        //
        // See if we can fold constants.
        //
    
        TIntermTyped* typedReturnNode = 0;
        if ( leftTempConstant && rightTempConstant) {
            typedReturnNode = leftTempConstant->fold(node->getOp(), rightTempConstant, infoSink);
    
            if (typedReturnNode)
                return typedReturnNode;
        }
    
        return node;
    }
    
    //
    // Connect two nodes through an assignment.
    //
    // Returns the added node.
    //
    TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line)
    {
        //
        // Like adding binary math, except the conversion can only go
        // from right to left.
        //
        TIntermBinary* node = new TIntermBinary(op);
        if (line == 0)
            line = left->getLine();
        node->setLine(line);
    
        TIntermTyped* child = addConversion(op, left->getType(), right);
        if (child == 0)
            return 0;
    
        node->setLeft(left);
        node->setRight(child);
        if (! node->promote(infoSink))
            return 0;
    
        return node;
    }
    
    //
    // Connect two nodes through an index operator, where the left node is the base
    // of an array or struct, and the right node is a direct or indirect offset.
    //
    // Returns the added node.
    // The caller should set the type of the returned node.
    //
    TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc line)
    {
        TIntermBinary* node = new TIntermBinary(op);
        if (line == 0)
            line = index->getLine();
        node->setLine(line);
        node->setLeft(base);
        node->setRight(index);
    
        // caller should set the type
    
        return node;
    }
    
    //
    // Add one node as the parent of another that it operates on.
    //
    // Returns the added node.
    //
    TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermNode* childNode, TSourceLoc line, TSymbolTable& symbolTable)
    {
        TIntermUnary* node;
        TIntermTyped* child = childNode->getAsTyped();
    
        if (child == 0) {
            infoSink.info.message(EPrefixInternalError, "Bad type in AddUnaryMath", line);
            return 0;
        }
    
        switch (op) {
            case EOpLogicalNot:
                if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) {
                    return 0;
                }
                break;
    
            case EOpPostIncrement:
            case EOpPreIncrement:
            case EOpPostDecrement:
            case EOpPreDecrement:
            case EOpNegative:
                if (child->getType().getBasicType() == EbtStruct || child->getType().isArray())
                    return 0;
            default: break;
        }
    
        //
        // Do we need to promote the operand?
        //
        // Note: Implicit promotions were removed from the language.
        //
        TBasicType newType = EbtVoid;
        switch (op) {
            case EOpConstructInt:   newType = EbtInt;   break;
            case EOpConstructBool:  newType = EbtBool;  break;
            case EOpConstructFloat: newType = EbtFloat; break;
            default: break;
        }
    
        if (newType != EbtVoid) {
            child = addConversion(op, TType(newType, child->getPrecision(), EvqTemporary,
                child->getNominalSize(),
                child->isMatrix(),
                child->isArray()),
                child);
            if (child == 0)
                return 0;
        }
    
        //
        // For constructors, we are now done, it's all in the conversion.
        //
        switch (op) {
            case EOpConstructInt:
            case EOpConstructBool:
            case EOpConstructFloat:
                return child;
            default: break;
        }
    
        TIntermConstantUnion *childTempConstant = 0;
        if (child->getAsConstantUnion())
            childTempConstant = child->getAsConstantUnion();
    
        //
        // Make a new node for the operator.
        //
        node = new TIntermUnary(op);
        if (line == 0)
            line = child->getLine();
        node->setLine(line);
        node->setOperand(child);
    
        if (! node->promote(infoSink))
            return 0;
    
        if (childTempConstant)  {
            TIntermTyped* newChild = childTempConstant->fold(op, 0, infoSink);
    
            if (newChild)
                return newChild;
        }
    
        return node;
    }
    
    //
    // This is the safe way to change the operator on an aggregate, as it
    // does lots of error checking and fixing.  Especially for establishing
    // a function call's operation on it's set of parameters.  Sequences
    // of instructions are also aggregates, but they just direnctly set
    // their operator to EOpSequence.
    //
    // Returns an aggregate node, which could be the one passed in if
    // it was already an aggregate.
    //
    TIntermAggregate* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, TSourceLoc line)
    {
        TIntermAggregate* aggNode;
    
        //
        // Make sure we have an aggregate.  If not turn it into one.
        //
        if (node) {
            aggNode = node->getAsAggregate();
            if (aggNode == 0 || aggNode->getOp() != EOpNull) {
                //
                // Make an aggregate containing this node.
                //
                aggNode = new TIntermAggregate();
                aggNode->getSequence().push_back(node);
                if (line == 0)
                    line = node->getLine();
            }
        } else
            aggNode = new TIntermAggregate();
    
        //
        // Set the operator.
        //
        aggNode->setOperator(op);
        if (line != 0)
            aggNode->setLine(line);
    
        return aggNode;
    }
    
    //
    // Convert one type to another.
    //
    // Returns the node representing the conversion, which could be the same
    // node passed in if no conversion was needed.
    //
    // Return 0 if a conversion can't be done.
    //
    TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node)
    {
        //
        // Does the base type allow operation?
        //
        switch (node->getBasicType()) {
            case EbtVoid:
            case EbtSampler2D:
            case EbtSamplerCube:
                return 0;
            default: break;
        }
    
        //
        // Otherwise, if types are identical, no problem
        //
        if (type == node->getType())
            return node;
    
        //
        // If one's a structure, then no conversions.
        //
        if (type.getStruct() || node->getType().getStruct())
            return 0;
    
        //
        // If one's an array, then no conversions.
        //
        if (type.isArray() || node->getType().isArray())
            return 0;
    
        TBasicType promoteTo;
    
        switch (op) {
            //
            // Explicit conversions
            //
            case EOpConstructBool:
                promoteTo = EbtBool;
                break;
            case EOpConstructFloat:
                promoteTo = EbtFloat;
                break;
            case EOpConstructInt:
                promoteTo = EbtInt;
                break;
            default:
                //
                // implicit conversions were removed from the language.
                //
                if (type.getBasicType() != node->getType().getBasicType())
                    return 0;
                //
                // Size and structure could still differ, but that's
                // handled by operator promotion.
                //
                return node;
        }
    
        if (node->getAsConstantUnion()) {
    
            return (promoteConstantUnion(promoteTo, node->getAsConstantUnion()));
        } else {
    
            //
            // Add a new newNode for the conversion.
            //
            TIntermUnary* newNode = 0;
    
            TOperator newOp = EOpNull;
            switch (promoteTo) {
                case EbtFloat:
                    switch (node->getBasicType()) {
                        case EbtInt:   newOp = EOpConvIntToFloat;  break;
                        case EbtBool:  newOp = EOpConvBoolToFloat; break;
                        default:
                            infoSink.info.message(EPrefixInternalError, "Bad promotion node", node->getLine());
                            return 0;
                    }
                    break;
                case EbtBool:
                    switch (node->getBasicType()) {
                        case EbtInt:   newOp = EOpConvIntToBool;   break;
                        case EbtFloat: newOp = EOpConvFloatToBool; break;
                        default:
                            infoSink.info.message(EPrefixInternalError, "Bad promotion node", node->getLine());
                            return 0;
                    }
                    break;
                case EbtInt:
                    switch (node->getBasicType()) {
                        case EbtBool:   newOp = EOpConvBoolToInt;  break;
                        case EbtFloat:  newOp = EOpConvFloatToInt; break;
                        default:
                            infoSink.info.message(EPrefixInternalError, "Bad promotion node", node->getLine());
                            return 0;
                    }
                    break;
                default:
                    infoSink.info.message(EPrefixInternalError, "Bad promotion type", node->getLine());
                    return 0;
            }
    
            TType type(promoteTo, node->getPrecision(), EvqTemporary, node->getNominalSize(), node->isMatrix(), node->isArray());
            newNode = new TIntermUnary(newOp, type);
            newNode->setLine(node->getLine());
            newNode->setOperand(node);
    
            return newNode;
        }
    }
    
    //
    // Safe way to combine two nodes into an aggregate.  Works with null pointers,
    // a node that's not a aggregate yet, etc.
    //
    // Returns the resulting aggregate, unless 0 was passed in for
    // both existing nodes.
    //
    TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, TSourceLoc line)
    {
        if (left == 0 && right == 0)
            return 0;
    
        TIntermAggregate* aggNode = 0;
        if (left)
            aggNode = left->getAsAggregate();
        if (!aggNode || aggNode->getOp() != EOpNull) {
            aggNode = new TIntermAggregate;
            if (left)
                aggNode->getSequence().push_back(left);
        }
    
        if (right)
            aggNode->getSequence().push_back(right);
    
        if (line != 0)
            aggNode->setLine(line);
    
        return aggNode;
    }
    
    //
    // Turn an existing node into an aggregate.
    //
    // Returns an aggregate, unless 0 was passed in for the existing node.
    //
    TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, TSourceLoc line)
    {
        if (node == 0)
            return 0;
    
        TIntermAggregate* aggNode = new TIntermAggregate;
        aggNode->getSequence().push_back(node);
    
        if (line != 0)
            aggNode->setLine(line);
        else
            aggNode->setLine(node->getLine());
    
        return aggNode;
    }
    
    //
    // For "if" test nodes.  There are three children; a condition,
    // a true path, and a false path.  The two paths are in the
    // nodePair.
    //
    // Returns the selection node created.
    //
    TIntermNode* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, TSourceLoc line)
    {
        //
        // For compile time constant selections, prune the code and
        // test now.
        //
    
        if (cond->getAsTyped() && cond->getAsTyped()->getAsConstantUnion()) {
            if (cond->getAsTyped()->getAsConstantUnion()->getUnionArrayPointer()->getBConst())
                return nodePair.node1;
            else
                return nodePair.node2;
        }
    
        TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2);
        node->setLine(line);
    
        return node;
    }
    
    
    TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, TSourceLoc line)
    {
        if (left->getType().getQualifier() == EvqConst && right->getType().getQualifier() == EvqConst) {
            return right;
        } else {
            TIntermTyped *commaAggregate = growAggregate(left, right, line);
            commaAggregate->getAsAggregate()->setOperator(EOpComma);
            commaAggregate->setType(right->getType());
            commaAggregate->getTypePointer()->changeQualifier(EvqTemporary);
            return commaAggregate;
        }
    }
    
    //
    // For "?:" test nodes.  There are three children; a condition,
    // a true path, and a false path.  The two paths are specified
    // as separate parameters.
    //
    // Returns the selection node created, or 0 if one could not be.
    //
    TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, TSourceLoc line)
    {
        //
        // Get compatible types.
        //
        TIntermTyped* child = addConversion(EOpSequence, trueBlock->getType(), falseBlock);
        if (child)
            falseBlock = child;
        else {
            child = addConversion(EOpSequence, falseBlock->getType(), trueBlock);
            if (child)
                trueBlock = child;
            else
                return 0;
        }
    
        //
        // See if all the operands are constant, then fold it otherwise not.
        //
    
        if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) {
            if (cond->getAsConstantUnion()->getUnionArrayPointer()->getBConst())
                return trueBlock;
            else
                return falseBlock;
        }
    
        //
        // Make a selection node.
        //
        TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
        node->setLine(line);
    
        return node;
    }
    
    //
    // Constant terminal nodes.  Has a union that contains bool, float or int constants
    //
    // Returns the constant union node created.
    //
    
    TIntermConstantUnion* TIntermediate::addConstantUnion(ConstantUnion* unionArrayPointer, const TType& t, TSourceLoc line)
    {
        TIntermConstantUnion* node = new TIntermConstantUnion(unionArrayPointer, t);
        node->setLine(line);
    
        return node;
    }
    
    TIntermTyped* TIntermediate::addSwizzle(TVectorFields& fields, TSourceLoc line)
    {
    
        TIntermAggregate* node = new TIntermAggregate(EOpSequence);
    
        node->setLine(line);
        TIntermConstantUnion* constIntNode;
        TIntermSequence &sequenceVector = node->getSequence();
        ConstantUnion* unionArray;
    
        for (int i = 0; i < fields.num; i++) {
            unionArray = new ConstantUnion[1];
            unionArray->setIConst(fields.offsets[i]);
            constIntNode = addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), line);
            sequenceVector.push_back(constIntNode);
        }
    
        return node;
    }
    
    //
    // Create loop nodes.
    //
    TIntermNode* TIntermediate::addLoop(TIntermNode *init, TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, TSourceLoc line)
    {
        TIntermNode* node = new TIntermLoop(init, body, test, terminal, testFirst);
        node->setLine(line);
    
        return node;
    }
    
    //
    // Add branches.
    //
    TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TSourceLoc line)
    {
        return addBranch(branchOp, 0, line);
    }
    
    TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, TSourceLoc line)
    {
        TIntermBranch* node = new TIntermBranch(branchOp, expression);
        node->setLine(line);
    
        return node;
    }
    
    //
    // This is to be executed once the final root is put on top by the parsing
    // process.
    //
    bool TIntermediate::postProcess(TIntermNode* root, EShLanguage language)
    {
        if (root == 0)
            return true;
    
        //
        // First, finish off the top level sequence, if any
        //
        TIntermAggregate* aggRoot = root->getAsAggregate();
        if (aggRoot && aggRoot->getOp() == EOpNull)
            aggRoot->setOperator(EOpSequence);
    
        return true;
    }
    
    //
    // This deletes the tree.
    //
    void TIntermediate::remove(TIntermNode* root)
    {
        if (root)
            RemoveAllTreeNodes(root);
    }
    
    ////////////////////////////////////////////////////////////////
    //
    // Member functions of the nodes used for building the tree.
    //
    ////////////////////////////////////////////////////////////////
    
    //
    // Say whether or not an operation node changes the value of a variable.
    //
    // Returns true if state is modified.
    //
    bool TIntermOperator::modifiesState() const
    {
        switch (op) {
            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:
                return true;
            default:
                return false;
        }
    }
    
    //
    // returns true if the operator is for one of the constructors
    //
    bool TIntermOperator::isConstructor() const
    {
        switch (op) {
            case EOpConstructVec2:
            case EOpConstructVec3:
            case EOpConstructVec4:
            case EOpConstructMat2:
            case EOpConstructMat3:
            case EOpConstructMat4:
            case EOpConstructFloat:
            case EOpConstructIVec2:
            case EOpConstructIVec3:
            case EOpConstructIVec4:
            case EOpConstructInt:
            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.
    //
    // Returns false in nothing makes sense.
    //
    bool TIntermUnary::promote(TInfoSink&)
    {
        switch (op) {
            case EOpLogicalNot:
                if (operand->getBasicType() != EbtBool)
                    return false;
                break;
            case EOpNegative:
            case EOpPostIncrement:
            case EOpPostDecrement:
            case EOpPreIncrement:
            case EOpPreDecrement:
                if (operand->getBasicType() == EbtBool)
                    return false;
                break;
    
                // operators for built-ins are already type checked against their prototype
            case EOpAny:
            case EOpAll:
            case EOpVectorLogicalNot:
                return true;
    
            default:
                if (operand->getBasicType() != EbtFloat)
                    return false;
        }
    
        setType(operand->getType());
    
        return true;
    }
    
    //
    // Establishes the type of the resultant operation, as well as
    // makes the operator the correct one for the operands.
    //
    // Returns false if operator can't work on operands.
    //
    bool TIntermBinary::promote(TInfoSink& infoSink)
    {
        // GLSL ES 2.0 does not support implicit type casting.
        // So the basic type should always match.
        if (left->getBasicType() != right->getBasicType())
            return false;
    
        //
        // Base assumption:  just make the type the same as the left
        // operand.  Then only deviations from this need be coded.
        //
        setType(left->getType());
    
        // The result gets promoted to the highest precision.
        TPrecision higherPrecision = GetHigherPrecision(left->getPrecision(), right->getPrecision());
        getTypePointer()->changePrecision(higherPrecision);
    
        // Binary operations results in temporary variables unless both
        // operands are const.
        if (left->getQualifier() != EvqConst || right->getQualifier() != EvqConst) {
            getTypePointer()->changeQualifier(EvqTemporary);
        }
    
        //
        // Array operations.
        //
        if (left->isArray() || right->isArray()) {
            //
            // Arrays types have to be exact matches.
            //
            if (left->getType() != right->getType())
                return false;
    
            switch (op) {
                //
                // Promote to conditional
                //
                case EOpEqual:
                case EOpNotEqual:
                    setType(TType(EbtBool, EbpUndefined));
                    break;
    
                //
                // Set array information.
                //
                case EOpAssign:
                case EOpInitialize:
                    getTypePointer()->setArraySize(left->getType().getArraySize());
                    getTypePointer()->setArrayInformationType(left->getType().getArrayInformationType());
                    break;
    
                default:
                    return false;
            }
            return true;
        }
    
        int size = std::max(left->getNominalSize(), right->getNominalSize());
    
        //
        // All scalars. Code after this test assumes this case is removed!
        //
        if (size == 1) {
            switch (op) {
                //
                // 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 EOpLogicalOr:
                    // Both operands must be of type bool.
                    if (left->getBasicType() != EbtBool || right->getBasicType() != EbtBool)
                        return false;
                    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.
        // Are the sizes compatible?
        //
        if (left->getNominalSize() != right->getNominalSize()) {
            // If the nominal size of operands do not match:
            // One of them must be scalar.
            if (left->getNominalSize() != 1 && right->getNominalSize() != 1)
                return false;
            // Operator cannot be of type pure assignment.
            if (op == EOpAssign || op == EOpInitialize)
                return false;
        }
    
        //
        // Can these two operands be combined?
        //
        TBasicType basicType = left->getBasicType();
        switch (op) {
            case EOpMul:
                if (!left->isMatrix() && right->isMatrix()) {
                    if (left->isVector())
                        op = EOpVectorTimesMatrix;
                    else {
                        op = EOpMatrixTimesScalar;
                        setType(TType(basicType, higherPrecision, EvqTemporary, size, true));
                    }
                } else if (left->isMatrix() && !right->isMatrix()) {
                    if (right->isVector()) {
                        op = EOpMatrixTimesVector;
                        setType(TType(basicType, higherPrecision, EvqTemporary, size, false));
                    } else {
                        op = EOpMatrixTimesScalar;
                    }
                } else if (left->isMatrix() && right->isMatrix()) {
                    op = EOpMatrixTimesMatrix;
                } else if (!left->isMatrix() && !right->isMatrix()) {
                    if (left->isVector() && right->isVector()) {
                        // leave as component product
                    } else if (left->isVector() || right->isVector()) {
                        op = EOpVectorTimesScalar;
                        setType(TType(basicType, higherPrecision, EvqTemporary, size, false));
                    }
                } else {
                    infoSink.info.message(EPrefixInternalError, "Missing elses", getLine());
                    return false;
                }
                break;
            case EOpMulAssign:
                if (!left->isMatrix() && right->isMatrix()) {
                    if (left->isVector())
                        op = EOpVectorTimesMatrixAssign;
                    else {
                        return false;
                    }
                } else if (left->isMatrix() && !right->isMatrix()) {
                    if (right->isVector()) {
                        return false;
                    } else {
                        op = EOpMatrixTimesScalarAssign;
                    }
                } else if (left->isMatrix() && right->isMatrix()) {
                    op = EOpMatrixTimesMatrixAssign;
                } else if (!left->isMatrix() && !right->isMatrix()) {
                    if (left->isVector() && right->isVector()) {
                        // leave as component product
                    } else if (left->isVector() || right->isVector()) {
                        if (! left->isVector())
                            return false;
                        op = EOpVectorTimesScalarAssign;
                        setType(TType(basicType, higherPrecision, EvqTemporary, size, false));
                    }
                } else {
                    infoSink.info.message(EPrefixInternalError, "Missing elses", getLine());
                    return false;
                }
                break;
    
            case EOpAssign:
            case EOpInitialize:
            case EOpAdd:
            case EOpSub:
            case EOpDiv:
            case EOpAddAssign:
            case EOpSubAssign:
            case EOpDivAssign:
                if (left->isMatrix() && right->isVector() ||
                    left->isVector() && right->isMatrix())
                    return false;
                setType(TType(basicType, higherPrecision, EvqTemporary, size, left->isMatrix() || right->isMatrix()));
                break;
    
            case EOpEqual:
            case EOpNotEqual:
            case EOpLessThan:
            case EOpGreaterThan:
            case EOpLessThanEqual:
            case EOpGreaterThanEqual:
                if (left->isMatrix() && right->isVector() ||
                    left->isVector() && right->isMatrix())
                    return false;
                setType(TType(EbtBool, EbpUndefined));
                break;
    
            default:
                return false;
        }
        
        return true;
    }
    
    bool CompareStruct(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray)
    {
        TTypeList* fields = leftNodeType.getStruct();
    
        size_t structSize = fields->size();
        int index = 0;
    
        for (size_t j = 0; j < structSize; j++) {
            int size = (*fields)[j].type->getObjectSize();
            for (int 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, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray)
    {
        if (leftNodeType.isArray()) {
            TType typeWithoutArrayness = leftNodeType;
            typeWithoutArrayness.clearArrayness();
    
            int arraySize = leftNodeType.getArraySize();
    
            for (int i = 0; i < arraySize; ++i) {
                int offset = typeWithoutArrayness.getObjectSize() * i;
                if (!CompareStruct(typeWithoutArrayness, &rightUnionArray[offset], &leftUnionArray[offset]))
                    return false;
            }
        } else
            return CompareStruct(leftNodeType, rightUnionArray, leftUnionArray);
    
        return true;
    }
    
    //
    // The fold functions see if an operation on a constant can be done in place,
    // without generating run-time code.
    //
    // Returns the node to keep using, which may or may not be the node passed in.
    //
    
    TIntermTyped* TIntermConstantUnion::fold(TOperator op, TIntermTyped* constantNode, TInfoSink& infoSink)
    {
        ConstantUnion *unionArray = getUnionArrayPointer();
        int objectSize = getType().getObjectSize();
    
        if (constantNode) {  // binary operations
            TIntermConstantUnion *node = constantNode->getAsConstantUnion();
            ConstantUnion *rightUnionArray = node->getUnionArrayPointer();
            TType returnType = getType();
    
            // for a case like float f = 1.2 + vec4(2,3,4,5);
            if (constantNode->getType().getObjectSize() == 1 && objectSize > 1) {
                rightUnionArray = new ConstantUnion[objectSize];
                for (int i = 0; i < objectSize; ++i)
                    rightUnionArray[i] = *node->getUnionArrayPointer();
                returnType = getType();
            } else if (constantNode->getType().getObjectSize() > 1 && objectSize == 1) {
                // for a case like float f = vec4(2,3,4,5) + 1.2;
                unionArray = new ConstantUnion[constantNode->getType().getObjectSize()];
                for (int i = 0; i < constantNode->getType().getObjectSize(); ++i)
                    unionArray[i] = *getUnionArrayPointer();
                returnType = node->getType();
                objectSize = constantNode->getType().getObjectSize();
            }
    
            ConstantUnion* tempConstArray = 0;
            TIntermConstantUnion *tempNode;
    
            bool boolNodeFlag = false;
            switch(op) {
                case EOpAdd:
                    tempConstArray = new ConstantUnion[objectSize];
                    {// support MSVC++6.0
                        for (int i = 0; i < objectSize; i++)
                            tempConstArray[i] = unionArray[i] + rightUnionArray[i];
                    }
                    break;
                case EOpSub:
                    tempConstArray = new ConstantUnion[objectSize];
                    {// support MSVC++6.0
                        for (int i = 0; i < objectSize; i++)
                            tempConstArray[i] = unionArray[i] - rightUnionArray[i];
                    }
                    break;
    
                case EOpMul:
                case EOpVectorTimesScalar:
                case EOpMatrixTimesScalar:
                    tempConstArray = new ConstantUnion[objectSize];
                    {// support MSVC++6.0
                        for (int i = 0; i < objectSize; i++)
                            tempConstArray[i] = unionArray[i] * rightUnionArray[i];
                    }
                    break;
                case EOpMatrixTimesMatrix:
                    if (getType().getBasicType() != EbtFloat || node->getBasicType() != EbtFloat) {
                        infoSink.info.message(EPrefixInternalError, "Constant Folding cannot be done for matrix multiply", getLine());
                        return 0;
                    }
                    {// support MSVC++6.0
                        int size = getNominalSize();
                        tempConstArray = new ConstantUnion[size*size];
                        for (int row = 0; row < size; row++) {
                            for (int column = 0; column < size; column++) {
                                tempConstArray[size * column + row].setFConst(0.0f);
                                for (int i = 0; i < size; i++) {
                                    tempConstArray[size * column + row].setFConst(tempConstArray[size * column + row].getFConst() + unionArray[i * size + row].getFConst() * (rightUnionArray[column * size + i].getFConst()));
                                }
                            }
                        }
                    }
                    break;
                case EOpDiv:
                    tempConstArray = new ConstantUnion[objectSize];
                    {// support MSVC++6.0
                        for (int i = 0; i < objectSize; i++) {
                            switch (getType().getBasicType()) {
                case EbtFloat:
                    if (rightUnionArray[i] == 0.0f) {
                        infoSink.info.message(EPrefixWarning, "Divide by zero error during constant folding", getLine());
                        tempConstArray[i].setFConst(FLT_MAX);
                    } else
                        tempConstArray[i].setFConst(unionArray[i].getFConst() / rightUnionArray[i].getFConst());
                    break;
    
                case EbtInt:
                    if (rightUnionArray[i] == 0) {
                        infoSink.info.message(EPrefixWarning, "Divide by zero error during constant folding", getLine());
                        tempConstArray[i].setIConst(INT_MAX);
                    } else
                        tempConstArray[i].setIConst(unionArray[i].getIConst() / rightUnionArray[i].getIConst());
                    break;
                default:
                    infoSink.info.message(EPrefixInternalError, "Constant folding cannot be done for \"/\"", getLine());
                    return 0;
                            }
                        }
                    }
                    break;
    
                case EOpMatrixTimesVector:
                    if (node->getBasicType() != EbtFloat) {
                        infoSink.info.message(EPrefixInternalError, "Constant Folding cannot be done for matrix times vector", getLine());
                        return 0;
                    }
                    tempConstArray = new ConstantUnion[getNominalSize()];
    
                    {// support MSVC++6.0
                        for (int size = getNominalSize(), i = 0; i < size; i++) {
                            tempConstArray[i].setFConst(0.0f);
                            for (int j = 0; j < size; j++) {
                                tempConstArray[i].setFConst(tempConstArray[i].getFConst() + ((unionArray[j*size + i].getFConst()) * rightUnionArray[j].getFConst()));
                            }
                        }
                    }
    
                    tempNode = new TIntermConstantUnion(tempConstArray, node->getType());
                    tempNode->setLine(getLine());
    
                    return tempNode;
    
                case EOpVectorTimesMatrix:
                    if (getType().getBasicType() != EbtFloat) {
                        infoSink.info.message(EPrefixInternalError, "Constant Folding cannot be done for vector times matrix", getLine());
                        return 0;
                    }
    
                    tempConstArray = new ConstantUnion[getNominalSize()];
                    {// support MSVC++6.0
                        for (int size = getNominalSize(), i = 0; i < size; i++) {
                            tempConstArray[i].setFConst(0.0f);
                            for (int j = 0; j < size; j++) {
                                tempConstArray[i].setFConst(tempConstArray[i].getFConst() + ((unionArray[j].getFConst()) * rightUnionArray[i*size + j].getFConst()));
                            }
                        }
                    }
                    break;
    
                case EOpLogicalAnd: // this code is written for possible future use, will not get executed currently
                    tempConstArray = new ConstantUnion[objectSize];
                    {// support MSVC++6.0
                        for (int i = 0; i < objectSize; i++)
                            tempConstArray[i] = unionArray[i] && rightUnionArray[i];
                    }
                    break;
    
                case EOpLogicalOr: // this code is written for possible future use, will not get executed currently
                    tempConstArray = new ConstantUnion[objectSize];
                    {// support MSVC++6.0
                        for (int i = 0; i < objectSize; i++)
                            tempConstArray[i] = unionArray[i] || rightUnionArray[i];
                    }
                    break;
    
                case EOpLogicalXor:
                    tempConstArray = new ConstantUnion[objectSize];
                    {// support MSVC++6.0
                        for (int i = 0; i < objectSize; i++)
                            switch (getType().getBasicType()) {
                case EbtBool: tempConstArray[i].setBConst((unionArray[i] == rightUnionArray[i]) ? false : true); break;
                default: assert(false && "Default missing");
                        }
                    }
                    break;
    
                case EOpLessThan:
                    assert(objectSize == 1);
                    tempConstArray = new ConstantUnion[1];
                    tempConstArray->setBConst(*unionArray < *rightUnionArray);
                    returnType = TType(EbtBool, EbpUndefined, EvqConst);
                    break;
                case EOpGreaterThan:
                    assert(objectSize == 1);
                    tempConstArray = new ConstantUnion[1];
                    tempConstArray->setBConst(*unionArray > *rightUnionArray);
                    returnType = TType(EbtBool, EbpUndefined, EvqConst);
                    break;
                case EOpLessThanEqual:
                    {
                        assert(objectSize == 1);
                        ConstantUnion constant;
                        constant.setBConst(*unionArray > *rightUnionArray);
                        tempConstArray = new ConstantUnion[1];
                        tempConstArray->setBConst(!constant.getBConst());
                        returnType = TType(EbtBool, EbpUndefined, EvqConst);
                        break;
                    }
                case EOpGreaterThanEqual:
                    {
                        assert(objectSize == 1);
                        ConstantUnion constant;
                        constant.setBConst(*unionArray < *rightUnionArray);
                        tempConstArray = new ConstantUnion[1];
                        tempConstArray->setBConst(!constant.getBConst());
                        returnType = TType(EbtBool, EbpUndefined, EvqConst);
                        break;
                    }
    
                case EOpEqual:
                    if (getType().getBasicType() == EbtStruct) {
                        if (!CompareStructure(node->getType(), node->getUnionArrayPointer(), unionArray))
                            boolNodeFlag = true;
                    } else {
                        for (int i = 0; i < objectSize; i++) {
                            if (unionArray[i] != rightUnionArray[i]) {
                                boolNodeFlag = true;
                                break;  // break out of for loop
                            }
                        }
                    }
    
                    tempConstArray = new ConstantUnion[1];
                    if (!boolNodeFlag) {
                        tempConstArray->setBConst(true);
                    }
                    else {
                        tempConstArray->setBConst(false);
                    }
    
                    tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EbpUndefined, EvqConst));
                    tempNode->setLine(getLine());
    
                    return tempNode;
    
                case EOpNotEqual:
                    if (getType().getBasicType() == EbtStruct) {
                        if (CompareStructure(node->getType(), node->getUnionArrayPointer(), unionArray))
                            boolNodeFlag = true;
                    } else {
                        for (int i = 0; i < objectSize; i++) {
                            if (unionArray[i] == rightUnionArray[i]) {
                                boolNodeFlag = true;
                                break;  // break out of for loop
                            }
                        }
                    }
    
                    tempConstArray = new ConstantUnion[1];
                    if (!boolNodeFlag) {
                        tempConstArray->setBConst(true);
                    }
                    else {
                        tempConstArray->setBConst(false);
                    }
    
                    tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EbpUndefined, EvqConst));
                    tempNode->setLine(getLine());
    
                    return tempNode;
    
                default:
                    infoSink.info.message(EPrefixInternalError, "Invalid operator for constant folding", getLine());
                    return 0;
            }
            tempNode = new TIntermConstantUnion(tempConstArray, returnType);
            tempNode->setLine(getLine());
    
            return tempNode;
        } else {
            //
            // Do unary operations
            //
            TIntermConstantUnion *newNode = 0;
            ConstantUnion* tempConstArray = new ConstantUnion[objectSize];
            for (int i = 0; i < objectSize; i++) {
                switch(op) {
                    case EOpNegative:
                        switch (getType().getBasicType()) {
                            case EbtFloat: tempConstArray[i].setFConst(-unionArray[i].getFConst()); break;
                            case EbtInt:   tempConstArray[i].setIConst(-unionArray[i].getIConst()); break;
                            default:
                                infoSink.info.message(EPrefixInternalError, "Unary operation not folded into constant", getLine());
                                return 0;
                        }
                        break;
                    case EOpLogicalNot: // this code is written for possible future use, will not get executed currently
                        switch (getType().getBasicType()) {
                            case EbtBool:  tempConstArray[i].setBConst(!unionArray[i].getBConst()); break;
                            default:
                                infoSink.info.message(EPrefixInternalError, "Unary operation not folded into constant", getLine());
                                return 0;
                        }
                        break;
                    default:
                        return 0;
                }
            }
            newNode = new TIntermConstantUnion(tempConstArray, getType());
            newNode->setLine(getLine());
            return newNode;
        }
    
        return this;
    }
    
    TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node)
    {
        ConstantUnion *rightUnionArray = node->getUnionArrayPointer();
        int size = node->getType().getObjectSize();
    
        ConstantUnion *leftUnionArray = new ConstantUnion[size];
    
        for (int i=0; i < size; i++) {
    
            switch (promoteTo) {
                case EbtFloat:
                    switch (node->getType().getBasicType()) {
                        case EbtInt:
                            leftUnionArray[i].setFConst(static_cast<float>(rightUnionArray[i].getIConst()));
                            break;
                        case EbtBool:
                            leftUnionArray[i].setFConst(static_cast<float>(rightUnionArray[i].getBConst()));
                            break;
                        case EbtFloat:
                            leftUnionArray[i] = rightUnionArray[i];
                            break;
                        default:
                            infoSink.info.message(EPrefixInternalError, "Cannot promote", node->getLine());
                            return 0;
                    }
                    break;
                case EbtInt:
                    switch (node->getType().getBasicType()) {
                        case EbtInt:
                            leftUnionArray[i] = rightUnionArray[i];
                            break;
                        case EbtBool:
                            leftUnionArray[i].setIConst(static_cast<int>(rightUnionArray[i].getBConst()));
                            break;
                        case EbtFloat:
                            leftUnionArray[i].setIConst(static_cast<int>(rightUnionArray[i].getFConst()));
                            break;
                        default:
                            infoSink.info.message(EPrefixInternalError, "Cannot promote", node->getLine());
                            return 0;
                    }
                    break;
                case EbtBool:
                    switch (node->getType().getBasicType()) {
                        case EbtInt:
                            leftUnionArray[i].setBConst(rightUnionArray[i].getIConst() != 0);
                            break;
                        case EbtBool:
                            leftUnionArray[i] = rightUnionArray[i];
                            break;
                        case EbtFloat:
                            leftUnionArray[i].setBConst(rightUnionArray[i].getFConst() != 0.0f);
                            break;
                        default:
                            infoSink.info.message(EPrefixInternalError, "Cannot promote", node->getLine());
                            return 0;
                    }
    
                    break;
                default:
                    infoSink.info.message(EPrefixInternalError, "Incorrect data type found", node->getLine());
                    return 0;
            }
    
        }
    
        const TType& t = node->getType();
    
        return addConstantUnion(leftUnionArray, TType(promoteTo, t.getPrecision(), t.getQualifier(), t.getNominalSize(), t.isMatrix(), t.isArray()), node->getLine());
    }
    
    void TIntermAggregate::addToPragmaTable(const TPragmaTable& pTable)
    {
        assert(!pragmaTable);
        pragmaTable = new TPragmaTable();
        *pragmaTable = pTable;
    }