//
// Copyright 2019 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.
//
// RewriteRowMajorMatrices: Rewrite row-major matrices as column-major.
//

#include "compiler/translator/tree_ops/glsl/apple/RewriteRowMajorMatrices.h"

#include "common/span.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/ImmutableStringBuilder.h"
#include "compiler/translator/StaticType.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/tree_util/ReplaceVariable.h"

namespace sh
{
namespace
{
// Only structs with matrices are tracked.  If layout(row_major) is applied to a struct that doesn't
// have matrices, it's silently dropped.  This is also used to avoid creating duplicates for inner
// structs that don't have matrices.
struct StructConversionData
{
    // The converted struct with every matrix transposed.
    TStructure *convertedStruct = nullptr;

    // The copy-from and copy-to functions copying from a struct to its converted version and back.
    TFunction *copyFromOriginal = nullptr;
    TFunction *copyToOriginal   = nullptr;
};

bool DoesFieldContainRowMajorMatrix(const TField *field, bool isBlockRowMajor)
{
    TLayoutMatrixPacking matrixPacking = field->type()->getLayoutQualifier().matrixPacking;

    // The field is row major if either explicitly specified as such, or if it inherits it from the
    // block layout qualifier.
    if (matrixPacking == EmpColumnMajor || (matrixPacking == EmpUnspecified && !isBlockRowMajor))
    {
        return false;
    }

    // The field is qualified with row_major, but if it's not a matrix or a struct containing
    // matrices, that's a useless qualifier.
    const TType *type = field->type();
    return type->isMatrix() || type->isStructureContainingMatrices();
}

TField *DuplicateField(const TField *field)
{
    return new TField(new TType(*field->type()), field->name(), field->line(), field->symbolType());
}

void SetColumnMajor(TType *type)
{
    TLayoutQualifier layoutQualifier = type->getLayoutQualifier();
    layoutQualifier.matrixPacking    = EmpColumnMajor;
    type->setLayoutQualifier(layoutQualifier);
}

TType *TransposeMatrixType(const TType *type)
{
    TType *newType = new TType(*type);

    SetColumnMajor(newType);

    newType->setPrimarySize(type->getRows());
    newType->setSecondarySize(type->getCols());

    return newType;
}

void CopyArraySizes(const TType *from, TType *to)
{
    if (from->isArray())
    {
        to->makeArrays(from->getArraySizes());
    }
}

// Determine if the node is an index node (array index or struct field selection).  For the purposes
// of this transformation, swizzle nodes are considered index nodes too.
bool IsIndexNode(TIntermNode *node, TIntermNode *child)
{
    if (node->getAsSwizzleNode())
    {
        return true;
    }

    TIntermBinary *binaryNode = node->getAsBinaryNode();
    if (binaryNode == nullptr || child != binaryNode->getLeft())
    {
        return false;
    }

    TOperator op = binaryNode->getOp();

    return op == EOpIndexDirect || op == EOpIndexDirectInterfaceBlock ||
           op == EOpIndexDirectStruct || op == EOpIndexIndirect;
}

TIntermSymbol *CopyToTempVariable(TSymbolTable *symbolTable,
                                  TIntermTyped *node,
                                  TIntermSequence *prependStatements)
{
    TVariable *temp              = CreateTempVariable(symbolTable, &node->getType(), EvqTemporary);
    TIntermDeclaration *tempDecl = CreateTempInitDeclarationNode(temp, node);
    prependStatements->push_back(tempDecl);

    return new TIntermSymbol(temp);
}

TIntermAggregate *CreateStructCopyCall(const TFunction *copyFunc, TIntermTyped *expression)
{
    TIntermSequence args = {expression};
    return TIntermAggregate::CreateFunctionCall(*copyFunc, &args);
}

TIntermTyped *CreateTransposeCall(TSymbolTable *symbolTable, TIntermTyped *expression)
{
    TIntermSequence args = {expression};
    return CreateBuiltInFunctionCallNode("transpose", &args, *symbolTable, 300);
}

TOperator GetIndex(TSymbolTable *symbolTable,
                   TIntermNode *node,
                   TIntermSequence *indices,
                   TIntermSequence *prependStatements)
{
    // Swizzle nodes are converted EOpIndexDirect for simplicity, with one index per swizzle
    // channel.
    TIntermSwizzle *asSwizzle = node->getAsSwizzleNode();
    if (asSwizzle)
    {
        for (uint32_t channel : asSwizzle->getSwizzleOffsets())
        {
            indices->push_back(CreateIndexNode(channel));
        }
        return EOpIndexDirect;
    }

    TIntermBinary *binaryNode = node->getAsBinaryNode();
    ASSERT(binaryNode);

    TOperator op = binaryNode->getOp();
    ASSERT(op == EOpIndexDirect || op == EOpIndexDirectInterfaceBlock ||
           op == EOpIndexDirectStruct || op == EOpIndexIndirect);

    TIntermTyped *rhs = binaryNode->getRight()->deepCopy();
    if (rhs->getAsConstantUnion() == nullptr)
    {
        rhs = CopyToTempVariable(symbolTable, rhs, prependStatements);
    }

    indices->push_back(rhs);
    return op;
}

TIntermTyped *ReplicateIndexNode(TSymbolTable *symbolTable,
                                 TIntermNode *node,
                                 TIntermTyped *lhs,
                                 TIntermSequence *indices)
{
    TIntermSwizzle *asSwizzle = node->getAsSwizzleNode();
    if (asSwizzle)
    {
        return new TIntermSwizzle(lhs, asSwizzle->getSwizzleOffsets());
    }

    TIntermBinary *binaryNode = node->getAsBinaryNode();
    ASSERT(binaryNode);

    ASSERT(indices->size() == 1);
    TIntermTyped *rhs = indices->front()->getAsTyped();

    return new TIntermBinary(binaryNode->getOp(), lhs, rhs);
}

TOperator GetIndexOp(TIntermNode *node)
{
    return node->getAsConstantUnion() ? EOpIndexDirect : EOpIndexIndirect;
}

bool IsConvertedField(TIntermTyped *indexNode,
                      const angle::HashMap<const TField *, bool> &convertedFields)
{
    TIntermBinary *asBinary = indexNode->getAsBinaryNode();
    if (asBinary == nullptr)
    {
        return false;
    }

    if (asBinary->getOp() != EOpIndexDirectInterfaceBlock)
    {
        return false;
    }

    const TInterfaceBlock *interfaceBlock = asBinary->getLeft()->getType().getInterfaceBlock();
    ASSERT(interfaceBlock);

    TIntermConstantUnion *fieldIndexNode = asBinary->getRight()->getAsConstantUnion();
    ASSERT(fieldIndexNode);
    ASSERT(fieldIndexNode->getConstantValue() != nullptr);

    int fieldIndex      = fieldIndexNode->getConstantValue()->getIConst();
    const TField *field = interfaceBlock->fields()[fieldIndex];

    return convertedFields.count(field) > 0 && convertedFields.at(field);
}

// A helper class to transform expressions of array type.  Iterates over every element of the
// array.
class TransformArrayHelper
{
  public:
    TransformArrayHelper(TIntermTyped *baseExpression)
        : mBaseExpression(baseExpression),
          mBaseExpressionType(baseExpression->getType()),
          mArrayIndices(mBaseExpressionType.getArraySizes().size(), 0)
    {}

    TIntermTyped *getNextElement(TIntermTyped *valueExpression, TIntermTyped **valueElementOut)
    {
        const angle::Span<const unsigned int> &arraySizes = mBaseExpressionType.getArraySizes();

        // If the last index overflows, element enumeration is done.
        if (mArrayIndices.back() >= arraySizes.back())
        {
            return nullptr;
        }

        TIntermTyped *element = getCurrentElement(mBaseExpression);
        if (valueExpression)
        {
            *valueElementOut = getCurrentElement(valueExpression);
        }

        incrementIndices(arraySizes);
        return element;
    }

    void accumulateForRead(TSymbolTable *symbolTable,
                           TIntermTyped *transformedElement,
                           TIntermSequence *prependStatements)
    {
        TIntermTyped *temp = CopyToTempVariable(symbolTable, transformedElement, prependStatements);
        mReadTransformConstructorArgs.push_back(temp);
    }

    TIntermTyped *constructReadTransformExpression()
    {
        const angle::Span<const unsigned int> &baseTypeArraySizes =
            mBaseExpressionType.getArraySizes();
        TVector<unsigned int> arraySizes(baseTypeArraySizes.begin(), baseTypeArraySizes.end());
        TIntermTyped *firstElement = mReadTransformConstructorArgs.front()->getAsTyped();
        const TType &baseType      = firstElement->getType();

        // If N dimensions, acc[0] == size[0] and acc[i] == size[i] * acc[i-1].
        // The last value is unused, and is not present.
        TVector<unsigned int> accumulatedArraySizes(arraySizes.size() - 1);

        if (accumulatedArraySizes.size() > 0)
        {
            accumulatedArraySizes[0] = arraySizes[0];
        }
        for (size_t index = 1; index + 1 < arraySizes.size(); ++index)
        {
            accumulatedArraySizes[index] = accumulatedArraySizes[index - 1] * arraySizes[index];
        }

        return constructReadTransformExpressionHelper(arraySizes, accumulatedArraySizes, baseType,
                                                      0);
    }

  private:
    TIntermTyped *getCurrentElement(TIntermTyped *expression)
    {
        TIntermTyped *element = expression->deepCopy();
        for (auto it = mArrayIndices.rbegin(); it != mArrayIndices.rend(); ++it)
        {
            unsigned int index = *it;
            element            = new TIntermBinary(EOpIndexDirect, element, CreateIndexNode(index));
        }
        return element;
    }

    void incrementIndices(const angle::Span<const unsigned int> &arraySizes)
    {
        // Assume mArrayIndices is an N digit number, where digit i is in the range
        // [0, arraySizes[i]).  This function increments this number.  Last digit is the most
        // significant digit.
        for (size_t digitIndex = 0; digitIndex < arraySizes.size(); ++digitIndex)
        {
            ++mArrayIndices[digitIndex];
            if (mArrayIndices[digitIndex] < arraySizes[digitIndex])
            {
                break;
            }
            if (digitIndex + 1 != arraySizes.size())
            {
                // This digit has now overflown and is reset to 0, carry will be added to the next
                // digit.  The most significant digit will keep the overflow though, to make it
                // clear we have exhausted the range.
                mArrayIndices[digitIndex] = 0;
            }
        }
    }

    TIntermTyped *constructReadTransformExpressionHelper(
        const TVector<unsigned int> &arraySizes,
        const TVector<unsigned int> &accumulatedArraySizes,
        const TType &baseType,
        size_t elementsOffset)
    {
        ASSERT(!arraySizes.empty());

        TType *transformedType = new TType(baseType);
        transformedType->makeArrays(arraySizes);

        // If one dimensional, create the constructor with the given elements.
        if (arraySizes.size() == 1)
        {
            ASSERT(accumulatedArraySizes.size() == 0);

            auto sliceStart = mReadTransformConstructorArgs.begin() + elementsOffset;
            TIntermSequence slice(sliceStart, sliceStart + arraySizes[0]);

            return TIntermAggregate::CreateConstructor(*transformedType, &slice);
        }

        // If not, create constructors for every column recursively.
        TVector<unsigned int> subArraySizes(arraySizes.begin(), arraySizes.end() - 1);
        TVector<unsigned int> subArrayAccumulatedSizes(accumulatedArraySizes.begin(),
                                                       accumulatedArraySizes.end() - 1);

        TIntermSequence constructorArgs;
        unsigned int colStride = accumulatedArraySizes.back();
        for (size_t col = 0; col < arraySizes.back(); ++col)
        {
            size_t colElementsOffset = elementsOffset + col * colStride;

            constructorArgs.push_back(constructReadTransformExpressionHelper(
                subArraySizes, subArrayAccumulatedSizes, baseType, colElementsOffset));
        }

        return TIntermAggregate::CreateConstructor(*transformedType, &constructorArgs);
    }

    TIntermTyped *mBaseExpression;
    const TType &mBaseExpressionType;
    TVector<unsigned int> mArrayIndices;

    TIntermSequence mReadTransformConstructorArgs;
};

// Traverser that:
//
// 1. Converts |layout(row_major) matCxR M| to |layout(column_major) matRxC Mt|.
// 2. Converts |layout(row_major) S s| to |layout(column_major) St st|, where S is a struct that
//    contains matrices, and St is a new struct with the transformation in 1 applied to matrix
//    members (recursively).
// 3. When read from, the following transformations are applied:
//
//            M       -> transpose(Mt)
//            M[c]    -> gvecN(Mt[0][c], Mt[1][c], ..., Mt[N-1][c])
//            M[c][r] -> Mt[r][c]
//            M[c].yz -> gvec2(Mt[1][c], Mt[2][c])
//            MArr    -> MType[D1]..[DN](transpose(MtArr[0]...[0]), ...)
//            s       -> copy_St_to_S(st)
//            sArr    -> SType[D1]...[DN](copy_St_to_S(stArr[0]..[0]), ...)
//            (matrix reads through struct are transformed similarly to M)
//
// 4. When written to, the following transformations are applied:
//
//      M = exp       -> Mt = transpose(exp)
//      M[c] = exp    -> temp = exp
//                       Mt[0][c] = temp[0]
//                       Mt[1][c] = temp[1]
//                       ...
//                       Mt[N-1][c] = temp[N-1]
//      M[c][r] = exp -> Mt[r][c] = exp
//      M[c].yz = exp -> temp = exp
//                       Mt[1][c] = temp[0]
//                       Mt[2][c] = temp[1]
//      MArr = exp    -> temp = exp
//                       Mt = MtType[D1]..[DN](temp([0]...[0]), ...)
//      s = exp       -> st = copy_S_to_St(exp)
//      sArr = exp    -> temp = exp
//                       St = StType[D1]...[DN](copy_S_to_St(temp[0]..[0]), ...)
//      (matrix writes through struct are transformed similarly to M)
//
// 5. If any of the above is passed to an `inout` parameter, both transformations are applied:
//
//            f(M[c]) -> temp = gvecN(Mt[0][c], Mt[1][c], ..., Mt[N-1][c])
//                       f(temp)
//                       Mt[0][c] = temp[0]
//                       Mt[1][c] = temp[1]
//                       ...
//                       Mt[N-1][c] = temp[N-1]
//
//               f(s) -> temp = copy_St_to_S(st)
//                       f(temp)
//                       st = copy_S_to_St(temp)
//
//    If passed to an `out` parameter, the `temp` parameter is simply not initialized.
//
// 6. If the expression leading to the matrix or struct has array subscripts, temp values are
//    created for them to avoid duplicating side effects.
//
class RewriteRowMajorMatricesTraverser : public TIntermTraverser
{
  public:
    RewriteRowMajorMatricesTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
        : TIntermTraverser(true, true, true, symbolTable),
          mCompiler(compiler),
          mStructMapOut(&mOuterPass.structMap),
          mInterfaceBlockMap(&mOuterPass.interfaceBlockMap),
          mInterfaceBlockFieldConvertedIn(mOuterPass.interfaceBlockFieldConverted),
          mCopyFunctionDefinitionsOut(&mOuterPass.copyFunctionDefinitions),
          mOuterTraverser(nullptr),
          mInnerPassRoot(nullptr),
          mIsProcessingInnerPassSubtree(false)
    {}

    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
    {
        // No need to process declarations in inner passes.
        if (mInnerPassRoot != nullptr)
        {
            return true;
        }

        if (visit != PreVisit)
        {
            return true;
        }

        const TIntermSequence &sequence = *(node->getSequence());

        TIntermTyped *variable = sequence.front()->getAsTyped();
        const TType &type      = variable->getType();

        // If it's a struct declaration that has matrices, remember it.  If a row-major instance
        // of it is created, it will have to be converted.
        if (type.isStructSpecifier() && type.isStructureContainingMatrices())
        {
            const TStructure *structure = type.getStruct();
            ASSERT(structure);

            ASSERT(mOuterPass.structMap.count(structure) == 0);

            StructConversionData structData;
            mOuterPass.structMap[structure] = structData;

            return false;
        }

        // If it's an interface block, it may have to be converted if it contains any row-major
        // fields.
        if (type.isInterfaceBlock() && type.getInterfaceBlock()->containsMatrices())
        {
            const TInterfaceBlock *block = type.getInterfaceBlock();
            ASSERT(block);
            bool isBlockRowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor;

            const TFieldList &fields = block->fields();
            bool anyRowMajor         = isBlockRowMajor;

            for (const TField *field : fields)
            {
                if (DoesFieldContainRowMajorMatrix(field, isBlockRowMajor))
                {
                    anyRowMajor = true;
                    break;
                }
            }

            if (anyRowMajor)
            {
                convertInterfaceBlock(node);
            }

            return false;
        }

        return true;
    }

    void visitSymbol(TIntermSymbol *symbol) override
    {
        // If in inner pass, only process if the symbol is under that root.
        if (mInnerPassRoot != nullptr && !mIsProcessingInnerPassSubtree)
        {
            return;
        }

        const TVariable *variable = &symbol->variable();
        bool needsRewrite         = mInterfaceBlockMap->count(variable) != 0;

        // If it's a field of a nameless interface block, it may still need conversion.
        if (!needsRewrite)
        {
            // Nameless interface block field symbols have the interface block pointer set, but are
            // not interface blocks.
            if (symbol->getType().getInterfaceBlock() && !variable->getType().isInterfaceBlock())
            {
                needsRewrite = convertNamelessInterfaceBlockField(symbol);
            }
        }

        if (needsRewrite)
        {
            transformExpression(symbol);
        }
    }

    bool visitBinary(Visit visit, TIntermBinary *node) override
    {
        if (node == mInnerPassRoot)
        {
            // We only want to process the right-hand side of an assignment in inner passes.  When
            // visit is InVisit, the left-hand side is already processed, and the right-hand side is
            // next.  Set a flag to mark this duration.
            mIsProcessingInnerPassSubtree = visit == InVisit;
        }

        return true;
    }

    TIntermSequence *getStructCopyFunctions() { return &mOuterPass.copyFunctionDefinitions; }

  private:
    typedef angle::HashMap<const TStructure *, StructConversionData> StructMap;
    typedef angle::HashMap<const TVariable *, TVariable *> InterfaceBlockMap;
    typedef angle::HashMap<const TField *, bool> InterfaceBlockFieldConverted;

    RewriteRowMajorMatricesTraverser(
        TSymbolTable *symbolTable,
        RewriteRowMajorMatricesTraverser *outerTraverser,
        InterfaceBlockMap *interfaceBlockMap,
        const InterfaceBlockFieldConverted &interfaceBlockFieldConverted,
        StructMap *structMap,
        TIntermSequence *copyFunctionDefinitions,
        TIntermBinary *innerPassRoot)
        : TIntermTraverser(true, true, true, symbolTable),
          mStructMapOut(structMap),
          mInterfaceBlockMap(interfaceBlockMap),
          mInterfaceBlockFieldConvertedIn(interfaceBlockFieldConverted),
          mCopyFunctionDefinitionsOut(copyFunctionDefinitions),
          mOuterTraverser(outerTraverser),
          mInnerPassRoot(innerPassRoot),
          mIsProcessingInnerPassSubtree(false)
    {}

    void convertInterfaceBlock(TIntermDeclaration *node)
    {
        ASSERT(mInnerPassRoot == nullptr);

        const TIntermSequence &sequence = *(node->getSequence());

        TIntermTyped *variableNode   = sequence.front()->getAsTyped();
        const TType &type            = variableNode->getType();
        const TInterfaceBlock *block = type.getInterfaceBlock();
        ASSERT(block);

        bool isBlockRowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor;

        // Recreate the struct with its row-major fields converted to column-major equivalents.
        TIntermSequence newDeclarations;

        TFieldList *newFields = new TFieldList;
        for (const TField *field : block->fields())
        {
            TField *newField = nullptr;

            if (DoesFieldContainRowMajorMatrix(field, isBlockRowMajor))
            {
                newField = convertField(field, &newDeclarations);

                // Remember that this field was converted.
                mOuterPass.interfaceBlockFieldConverted[field] = true;
            }
            else
            {
                newField = DuplicateField(field);
            }

            newFields->push_back(newField);
        }

        // Create a new interface block with these fields.
        TLayoutQualifier blockLayoutQualifier = type.getLayoutQualifier();
        blockLayoutQualifier.matrixPacking    = EmpColumnMajor;

        TInterfaceBlock *newInterfaceBlock =
            new TInterfaceBlock(mSymbolTable, block->name(), newFields, blockLayoutQualifier,
                                block->symbolType(), block->extensions());

        // Create a new declaration with the new type.  Declarations are separated at this point,
        // so there should be only one variable here.
        ASSERT(sequence.size() == 1);

        TType *newInterfaceBlockType =
            new TType(newInterfaceBlock, type.getQualifier(), blockLayoutQualifier);

        TIntermDeclaration *newDeclaration = new TIntermDeclaration;
        const TVariable *variable          = &variableNode->getAsSymbolNode()->variable();

        const TType *newType = newInterfaceBlockType;
        if (type.isArray())
        {
            TType *newArrayType = new TType(*newType);
            CopyArraySizes(&type, newArrayType);
            newType = newArrayType;
        }

        // If the interface block variable itself is temp, use an empty name.
        bool variableIsTemp = variable->symbolType() == SymbolType::Empty;
        const ImmutableString &variableName =
            variableIsTemp ? kEmptyImmutableString : variable->name();

        TVariable *newVariable = new TVariable(mSymbolTable, variableName, newType,
                                               variable->symbolType(), variable->extensions());

        newDeclaration->appendDeclarator(new TIntermSymbol(newVariable));

        mOuterPass.interfaceBlockMap[variable] = newVariable;

        newDeclarations.push_back(newDeclaration);

        // Replace the interface block definition with the new one, prepending any new struct
        // definitions.
        mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node,
                                        std::move(newDeclarations));
    }

    bool convertNamelessInterfaceBlockField(TIntermSymbol *symbol)
    {
        const TVariable *variable             = &symbol->variable();
        const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();

        // Find the variable corresponding to this interface block.  If the interface block
        // is not rewritten, or this refers to a field that is not rewritten, there's
        // nothing to do.
        for (auto iter : *mInterfaceBlockMap)
        {
            // Skip other rewritten nameless interface block fields.
            if (!iter.first->getType().isInterfaceBlock())
            {
                continue;
            }

            // Skip if this is not a field of this rewritten interface block.
            if (iter.first->getType().getInterfaceBlock() != interfaceBlock)
            {
                continue;
            }

            const ImmutableString symbolName = symbol->getName();

            // Find which field it is
            const TVector<TField *> fields = interfaceBlock->fields();
            const size_t fieldIndex        = variable->getType().getInterfaceBlockFieldIndex();
            ASSERT(fieldIndex < fields.size());

            const TField *field = fields[fieldIndex];
            ASSERT(field->name() == symbolName);

            // If this field doesn't need a rewrite, there's nothing to do.
            if (mInterfaceBlockFieldConvertedIn.count(field) == 0 ||
                !mInterfaceBlockFieldConvertedIn.at(field))
            {
                break;
            }

            // Create a new variable that references the replaced interface block.
            TType *newType = new TType(variable->getType());
            newType->setInterfaceBlockField(iter.second->getType().getInterfaceBlock(), fieldIndex);

            TVariable *newVariable = new TVariable(mSymbolTable, variable->name(), newType,
                                                   variable->symbolType(), variable->extensions());

            (*mInterfaceBlockMap)[variable] = newVariable;

            return true;
        }

        return false;
    }

    void convertStruct(const TStructure *structure, TIntermSequence *newDeclarations)
    {
        ASSERT(mInnerPassRoot == nullptr);

        ASSERT(mOuterPass.structMap.count(structure) != 0);
        StructConversionData *structData = &mOuterPass.structMap[structure];

        if (structData->convertedStruct)
        {
            return;
        }

        TFieldList *newFields = new TFieldList;
        for (const TField *field : structure->fields())
        {
            newFields->push_back(convertField(field, newDeclarations));
        }

        // Create unique names for the converted structs.  We can't leave them nameless and have
        // a name autogenerated similar to temp variables, as nameless structs exist.  A fake
        // variable is created for the sole purpose of generating a temp name.
        TVariable *newStructTypeName =
            new TVariable(mSymbolTable, kEmptyImmutableString,
                          StaticType::GetBasic<EbtUInt, EbpUndefined>(), SymbolType::Empty);

        TStructure *newStruct = new TStructure(mSymbolTable, newStructTypeName->name(), newFields,
                                               SymbolType::AngleInternal);
        TType *newType        = new TType(newStruct, true);
        TVariable *newStructVar =
            new TVariable(mSymbolTable, kEmptyImmutableString, newType, SymbolType::Empty);

        TIntermDeclaration *structDecl = new TIntermDeclaration;
        structDecl->appendDeclarator(new TIntermSymbol(newStructVar));

        newDeclarations->push_back(structDecl);

        structData->convertedStruct = newStruct;
    }

    TField *convertField(const TField *field, TIntermSequence *newDeclarations)
    {
        ASSERT(mInnerPassRoot == nullptr);

        TField *newField = nullptr;

        const TType *fieldType = field->type();
        TType *newType         = nullptr;

        if (fieldType->isStructureContainingMatrices())
        {
            // If the field is a struct instance, convert the struct and replace the field
            // with an instance of the new struct.
            const TStructure *fieldTypeStruct = fieldType->getStruct();
            convertStruct(fieldTypeStruct, newDeclarations);

            StructConversionData &structData = mOuterPass.structMap[fieldTypeStruct];
            newType                          = new TType(structData.convertedStruct, false);
            SetColumnMajor(newType);
            CopyArraySizes(fieldType, newType);
        }
        else if (fieldType->isMatrix())
        {
            // If the field is a matrix, transpose the matrix and replace the field with
            // that, removing the matrix packing qualifier.
            newType = TransposeMatrixType(fieldType);
        }

        if (newType)
        {
            newField = new TField(newType, field->name(), field->line(), field->symbolType());
        }
        else
        {
            newField = DuplicateField(field);
        }

        return newField;
    }

    void determineAccess(TIntermNode *expression,
                         TIntermNode *accessor,
                         bool *isReadOut,
                         bool *isWriteOut)
    {
        // If passing to a function, look at whether the parameter is in, out or inout.
        TIntermAggregate *functionCall = accessor->getAsAggregate();

        if (functionCall)
        {
            TIntermSequence *arguments = functionCall->getSequence();
            for (size_t argIndex = 0; argIndex < arguments->size(); ++argIndex)
            {
                if ((*arguments)[argIndex] == expression)
                {
                    TQualifier qualifier = EvqParamIn;

                    // If the aggregate is not a function call, it's a constructor, and so every
                    // argument is an input.
                    const TFunction *function = functionCall->getFunction();
                    if (function)
                    {
                        const TVariable *param = function->getParam(argIndex);
                        qualifier              = param->getType().getQualifier();
                    }

                    *isReadOut  = qualifier != EvqParamOut;
                    *isWriteOut = qualifier == EvqParamOut || qualifier == EvqParamInOut;
                    break;
                }
            }
            return;
        }

        TIntermBinary *assignment = accessor->getAsBinaryNode();
        if (assignment && IsAssignment(assignment->getOp()))
        {
            // If expression is on the right of assignment, it's being read from.
            *isReadOut = assignment->getRight() == expression;
            // If it's on the left of assignment, it's being written to.
            *isWriteOut = assignment->getLeft() == expression;
            return;
        }

        // Any other usage is a read.
        *isReadOut  = true;
        *isWriteOut = false;
    }

    void transformExpression(TIntermSymbol *symbol)
    {
        // Walk up the parent chain while the nodes are EOpIndex* (whether array indexing or struct
        // field selection) or swizzle and construct the replacement expression.  This traversal can
        // lead to one of the following possibilities:
        //
        // - a.b[N].etc.s (struct, or struct array): copy function should be declared and used,
        // - a.b[N].etc.M (matrix or matrix array): transpose() should be used,
        // - a.b[N].etc.M[c] (a column): each element in column needs to be handled separately,
        // - a.b[N].etc.M[c].yz (multiple elements): similar to whole column, but a subset of
        //   elements,
        // - a.b[N].etc.M[c][r] (an element): single element to handle.
        // - a.b[N].etc.x (not struct or matrix): not modified
        //
        // primaryIndex will contain c, if any.  secondaryIndices will contain {0, ..., R-1}
        // (if no [r] or swizzle), {r} (if [r]), or {1, 2} (corresponding to .yz) if any.
        //
        // In all cases, the base symbol is replaced.  |baseExpression| will contain everything up
        // to (and not including) the last index/swizzle operations, i.e. a.b[N].etc.s/M/x.  Any
        // non constant array subscript is assigned to a temp variable to avoid duplicating side
        // effects.
        //
        // ---
        //
        // NOTE that due to the use of insertStatementsInParentBlock, cases like this will be
        // mistranslated, and this bug is likely present in most transformations that use this
        // feature:
        //
        //     if (x == 1 && a.b[x = 2].etc.M = value)
        //
        // which will translate to:
        //
        //     temp = (x = 2)
        //     if (x == 1 && a.b[temp].etc.M = transpose(value))
        //
        // See http://anglebug.com/42262472.
        //
        TIntermTyped *baseExpression =
            new TIntermSymbol(mInterfaceBlockMap->at(&symbol->variable()));
        const TStructure *structure = nullptr;

        TIntermNode *primaryIndex = nullptr;
        TIntermSequence secondaryIndices;

        // In some cases, it is necessary to prepend or append statements.  Those are captured in
        // |prependStatements| and |appendStatements|.
        TIntermSequence prependStatements;
        TIntermSequence appendStatements;

        // If the expression is neither a struct or matrix, no modification is necessary.
        // If it's a struct that doesn't have matrices, again there's no transformation necessary.
        // If it's an interface block matrix field that didn't need to be transposed, no
        // transpformation is necessary.
        //
        // In all these cases, |baseExpression| contains all of the original expression.
        //
        // If the starting symbol itself is a field of a nameless interface block, it needs
        // conversion if we reach here.
        bool requiresTransformation = !symbol->getType().isInterfaceBlock();

        uint32_t accessorIndex         = 0;
        TIntermTyped *previousAncestor = symbol;
        while (IsIndexNode(getAncestorNode(accessorIndex), previousAncestor))
        {
            TIntermTyped *ancestor = getAncestorNode(accessorIndex)->getAsTyped();
            ASSERT(ancestor);

            const TType &previousAncestorType = previousAncestor->getType();

            TIntermSequence indices;
            TOperator op = GetIndex(mSymbolTable, ancestor, &indices, &prependStatements);

            bool opIsIndex     = op == EOpIndexDirect || op == EOpIndexIndirect;
            bool isArrayIndex  = opIsIndex && previousAncestorType.isArray();
            bool isMatrixIndex = opIsIndex && previousAncestorType.isMatrix();

            // If it's a direct index in a matrix, it's the primary index.
            bool isMatrixPrimarySubscript = isMatrixIndex && !isArrayIndex;
            ASSERT(!isMatrixPrimarySubscript ||
                   (primaryIndex == nullptr && secondaryIndices.empty()));
            // If primary index is seen and the ancestor is still an index, it must be a direct
            // index as the secondary one.  Note that if primaryIndex is set, there can only ever be
            // one more parent of interest, and that's subscripting the second dimension.
            bool isMatrixSecondarySubscript = primaryIndex != nullptr;
            ASSERT(!isMatrixSecondarySubscript || (opIsIndex && !isArrayIndex));

            if (requiresTransformation && isMatrixPrimarySubscript)
            {
                ASSERT(indices.size() == 1);
                primaryIndex = indices.front();

                // Default the secondary indices to include every row.  If there's a secondary
                // subscript provided, it will override this.
                const uint8_t rows = previousAncestorType.getRows();
                for (uint8_t r = 0; r < rows; ++r)
                {
                    secondaryIndices.push_back(CreateIndexNode(r));
                }
            }
            else if (isMatrixSecondarySubscript)
            {
                ASSERT(requiresTransformation);

                secondaryIndices = indices;

                // Indices after this point are not interesting.  There can't actually be any other
                // index nodes.  Unlike desktop GLSL, ESSL does not support swizzles on scalars
                // (like M[1][2].yyy).
                ++accessorIndex;
                break;
            }
            else
            {
                // Replicate the expression otherwise.
                baseExpression =
                    ReplicateIndexNode(mSymbolTable, ancestor, baseExpression, &indices);

                const TType &ancestorType = ancestor->getType();
                structure                 = ancestorType.getStruct();

                requiresTransformation =
                    requiresTransformation ||
                    IsConvertedField(ancestor, mInterfaceBlockFieldConvertedIn);

                // If we reach a point where the expression is neither a matrix-containing struct
                // nor a matrix, there's no transformation required.  This can happen if we decend
                // through a struct marked with row-major but arrive at a member that doesn't
                // include a matrix.
                if (!ancestorType.isMatrix() && !ancestorType.isStructureContainingMatrices())
                {
                    requiresTransformation = false;
                }
            }

            previousAncestor = ancestor;
            ++accessorIndex;
        }

        TIntermNode *originalExpression =
            accessorIndex == 0 ? symbol : getAncestorNode(accessorIndex - 1);
        TIntermNode *accessor = getAncestorNode(accessorIndex);

        // if accessor is EOpArrayLength, we don't need to perform any transformations either.
        // Note that this only applies to unsized arrays, as the RemoveArrayLengthMethod()
        // transformation would have removed this operation otherwise.
        TIntermUnary *accessorAsUnary = accessor->getAsUnaryNode();
        if (requiresTransformation && accessorAsUnary && accessorAsUnary->getOp() == EOpArrayLength)
        {
            ASSERT(accessorAsUnary->getOperand() == originalExpression);
            ASSERT(accessorAsUnary->getOperand()->getType().isUnsizedArray());

            requiresTransformation = false;

            // We need to replace the whole expression including the EOpArrayLength, to avoid
            // confusing the replacement code as the original and new expressions don't have the
            // same type (one is the transpose of the other).  This doesn't affect the .length()
            // operation, so this replacement is ok, though it's not worth special-casing this in
            // the node replacement algorithm.
            //
            // Note: the |if (!requiresTransformation)| immediately below will be entered after
            // this.
            originalExpression = accessor;
            accessor           = getAncestorNode(accessorIndex + 1);
            baseExpression     = new TIntermUnary(EOpArrayLength, baseExpression, nullptr);
        }

        if (!requiresTransformation)
        {
            ASSERT(primaryIndex == nullptr);
            queueReplacementWithParent(accessor, originalExpression, baseExpression,
                                       OriginalNode::IS_DROPPED);

            RewriteRowMajorMatricesTraverser *traverser = mOuterTraverser ? mOuterTraverser : this;
            traverser->insertStatementsInParentBlock(prependStatements, appendStatements);
            return;
        }

        ASSERT(structure == nullptr || primaryIndex == nullptr);
        ASSERT(structure != nullptr || baseExpression->getType().isMatrix());

        // At the end, we can determine if the expression is being read from or written to (or both,
        // if sent as an inout parameter to a function).  For the sake of the transformation, the
        // left-hand side of operations like += can be treated as "written to", without necessarily
        // "read from".
        bool isRead  = false;
        bool isWrite = false;

        determineAccess(originalExpression, accessor, &isRead, &isWrite);

        ASSERT(isRead || isWrite);

        TIntermTyped *readExpression = nullptr;
        if (isRead)
        {
            readExpression = transformReadExpression(
                baseExpression, primaryIndex, &secondaryIndices, structure, &prependStatements);

            // If both read from and written to (i.e. passed to inout parameter), store the
            // expression in a temp variable and pass that to the function.
            if (isWrite)
            {
                readExpression =
                    CopyToTempVariable(mSymbolTable, readExpression, &prependStatements);
            }

            // Replace the original expression with the transformed one.  Read transformations
            // always generate a single expression that can be used in place of the original (as
            // oppposed to write transformations that can generate multiple statements).
            queueReplacementWithParent(accessor, originalExpression, readExpression,
                                       OriginalNode::IS_DROPPED);
        }

        TIntermSequence postTransformPrependStatements;
        TIntermSequence *writeStatements = &appendStatements;
        TOperator assignmentOperator     = EOpAssign;

        if (isWrite)
        {
            TIntermTyped *valueExpression = readExpression;

            if (!valueExpression)
            {
                // If there's already a read expression, this was an inout parameter and
                // |valueExpression| will contain the temp variable that was passed to the function
                // instead.
                //
                // If not, then the modification is either through being passed as an out parameter
                // to a function, or an assignment.  In the former case, create a temp variable to
                // be passed to the function.  In the latter case, create a temp variable that holds
                // the right hand side expression.
                //
                // In either case, use that temp value as the value to assign to |baseExpression|.

                TVariable *temp = CreateTempVariable(
                    mSymbolTable, &originalExpression->getAsTyped()->getType(), EvqTemporary);
                TIntermDeclaration *tempDecl = nullptr;

                valueExpression = new TIntermSymbol(temp);

                TIntermBinary *assignment = accessor->getAsBinaryNode();
                if (assignment)
                {
                    assignmentOperator = assignment->getOp();
                    ASSERT(IsAssignment(assignmentOperator));

                    // We are converting the assignment to the left-hand side of an expression in
                    // the form M=exp.  A subexpression of exp itself could require a
                    // transformation.  This complicates things as there would be two replacements:
                    //
                    // - Replace M=exp with temp (because the return value of the assignment could
                    //   be used)
                    // - Replace exp with exp2, where parent is M=exp
                    //
                    // The second replacement however is ineffective as the whole of M=exp is
                    // already transformed.  What's worse, M=exp is transformed without taking exp's
                    // transformations into account.  To address this issue, this same traverser is
                    // called on the right-hand side expression, with a special flag such that it
                    // only processes that expression.
                    //
                    RewriteRowMajorMatricesTraverser *outerTraverser =
                        mOuterTraverser ? mOuterTraverser : this;
                    RewriteRowMajorMatricesTraverser rhsTraverser(
                        mSymbolTable, outerTraverser, mInterfaceBlockMap,
                        mInterfaceBlockFieldConvertedIn, mStructMapOut, mCopyFunctionDefinitionsOut,
                        assignment);
                    getRootNode()->traverse(&rhsTraverser);
                    bool valid = rhsTraverser.updateTree(mCompiler, getRootNode());
                    ASSERT(valid);

                    tempDecl = CreateTempInitDeclarationNode(temp, assignment->getRight());

                    // Replace the whole assignment expression with the right-hand side as a read
                    // expression, in case the result of the assignment is used.  For example, this
                    // transforms:
                    //
                    //     if ((M += exp) == X)
                    //     {
                    //         // use M
                    //     }
                    //
                    // to:
                    //
                    //     temp = exp;
                    //     M += transform(temp);
                    //     if (transform(M) == X)
                    //     {
                    //         // use M
                    //     }
                    //
                    // Note that in this case the assignment to M must be prepended in the parent
                    // block.  In contrast, when sent to a function, the assignment to M should be
                    // done after the current function call is done.
                    //
                    // If the read from M itself (to replace assigmnet) needs to generate extra
                    // statements, they should be appended after the statements that write to M.
                    // These statements are stored in postTransformPrependStatements and appended to
                    // prependStatements in the end.
                    //
                    writeStatements = &prependStatements;

                    TIntermTyped *assignmentResultExpression = transformReadExpression(
                        baseExpression->deepCopy(), primaryIndex, &secondaryIndices, structure,
                        &postTransformPrependStatements);

                    // Replace the whole assignment, instead of just the right hand side.
                    TIntermNode *accessorParent = getAncestorNode(accessorIndex + 1);
                    queueReplacementWithParent(accessorParent, accessor, assignmentResultExpression,
                                               OriginalNode::IS_DROPPED);
                }
                else
                {
                    tempDecl = CreateTempDeclarationNode(temp);

                    // Replace the write expression (a function call argument) with the temp
                    // variable.
                    queueReplacementWithParent(accessor, originalExpression, valueExpression,
                                               OriginalNode::IS_DROPPED);
                }
                prependStatements.push_back(tempDecl);
            }

            if (isRead)
            {
                baseExpression = baseExpression->deepCopy();
            }
            transformWriteExpression(baseExpression, primaryIndex, &secondaryIndices, structure,
                                     valueExpression, assignmentOperator, writeStatements);
        }

        prependStatements.insert(prependStatements.end(), postTransformPrependStatements.begin(),
                                 postTransformPrependStatements.end());

        RewriteRowMajorMatricesTraverser *traverser = mOuterTraverser ? mOuterTraverser : this;
        traverser->insertStatementsInParentBlock(prependStatements, appendStatements);
    }

    TIntermTyped *transformReadExpression(TIntermTyped *baseExpression,
                                          TIntermNode *primaryIndex,
                                          TIntermSequence *secondaryIndices,
                                          const TStructure *structure,
                                          TIntermSequence *prependStatements)
    {
        const TType &baseExpressionType = baseExpression->getType();

        if (structure)
        {
            ASSERT(primaryIndex == nullptr && secondaryIndices->empty());
            ASSERT(mStructMapOut->count(structure) != 0);
            ASSERT((*mStructMapOut)[structure].convertedStruct != nullptr);

            // Declare copy-from-converted-to-original-struct function (if not already).
            declareStructCopyToOriginal(structure);

            const TFunction *copyToOriginal = (*mStructMapOut)[structure].copyToOriginal;

            if (baseExpressionType.isArray())
            {
                // If base expression is an array, transform every element.
                TransformArrayHelper transformHelper(baseExpression);

                TIntermTyped *element = nullptr;
                while ((element = transformHelper.getNextElement(nullptr, nullptr)) != nullptr)
                {
                    TIntermTyped *transformedElement =
                        CreateStructCopyCall(copyToOriginal, element);
                    transformHelper.accumulateForRead(mSymbolTable, transformedElement,
                                                      prependStatements);
                }
                return transformHelper.constructReadTransformExpression();
            }
            else
            {
                // If not reading an array, the result is simply a call to this function with the
                // base expression.
                return CreateStructCopyCall(copyToOriginal, baseExpression);
            }
        }

        // If not indexed, the result is transpose(exp)
        if (primaryIndex == nullptr)
        {
            ASSERT(secondaryIndices->empty());

            if (baseExpressionType.isArray())
            {
                // If array, transpose every element.
                TransformArrayHelper transformHelper(baseExpression);

                TIntermTyped *element = nullptr;
                while ((element = transformHelper.getNextElement(nullptr, nullptr)) != nullptr)
                {
                    TIntermTyped *transformedElement = CreateTransposeCall(mSymbolTable, element);
                    transformHelper.accumulateForRead(mSymbolTable, transformedElement,
                                                      prependStatements);
                }
                return transformHelper.constructReadTransformExpression();
            }
            else
            {
                return CreateTransposeCall(mSymbolTable, baseExpression);
            }
        }

        // If indexed the result is a vector (or just one element) where the primary and secondary
        // indices are swapped.
        ASSERT(!secondaryIndices->empty());

        TOperator primaryIndexOp          = GetIndexOp(primaryIndex);
        TIntermTyped *primaryIndexAsTyped = primaryIndex->getAsTyped();

        TIntermSequence transposedColumn;
        for (TIntermNode *secondaryIndex : *secondaryIndices)
        {
            TOperator secondaryIndexOp          = GetIndexOp(secondaryIndex);
            TIntermTyped *secondaryIndexAsTyped = secondaryIndex->getAsTyped();

            TIntermBinary *colIndexed = new TIntermBinary(
                secondaryIndexOp, baseExpression->deepCopy(), secondaryIndexAsTyped->deepCopy());
            TIntermBinary *colRowIndexed =
                new TIntermBinary(primaryIndexOp, colIndexed, primaryIndexAsTyped->deepCopy());

            transposedColumn.push_back(colRowIndexed);
        }

        if (secondaryIndices->size() == 1)
        {
            // If only one element, return that directly.
            return transposedColumn.front()->getAsTyped();
        }

        // Otherwise create a constructor with the appropriate dimension.
        TType *vecType = new TType(baseExpressionType.getBasicType(), secondaryIndices->size());
        return TIntermAggregate::CreateConstructor(*vecType, &transposedColumn);
    }

    void transformWriteExpression(TIntermTyped *baseExpression,
                                  TIntermNode *primaryIndex,
                                  TIntermSequence *secondaryIndices,
                                  const TStructure *structure,
                                  TIntermTyped *valueExpression,
                                  TOperator assignmentOperator,
                                  TIntermSequence *writeStatements)
    {
        const TType &baseExpressionType = baseExpression->getType();

        if (structure)
        {
            ASSERT(primaryIndex == nullptr && secondaryIndices->empty());
            ASSERT(mStructMapOut->count(structure) != 0);
            ASSERT((*mStructMapOut)[structure].convertedStruct != nullptr);

            // Declare copy-to-converted-from-original-struct function (if not already).
            declareStructCopyFromOriginal(structure);

            // The result is call to this function with the value expression assigned to base
            // expression.
            const TFunction *copyFromOriginal = (*mStructMapOut)[structure].copyFromOriginal;

            if (baseExpressionType.isArray())
            {
                // If array, assign every element.
                TransformArrayHelper transformHelper(baseExpression);

                TIntermTyped *element      = nullptr;
                TIntermTyped *valueElement = nullptr;
                while ((element = transformHelper.getNextElement(valueExpression, &valueElement)) !=
                       nullptr)
                {
                    TIntermTyped *functionCall =
                        CreateStructCopyCall(copyFromOriginal, valueElement);
                    writeStatements->push_back(new TIntermBinary(EOpAssign, element, functionCall));
                }
            }
            else
            {
                TIntermTyped *functionCall =
                    CreateStructCopyCall(copyFromOriginal, valueExpression->deepCopy());
                writeStatements->push_back(
                    new TIntermBinary(EOpAssign, baseExpression, functionCall));
            }

            return;
        }

        // If not indexed, the result is transpose(exp)
        if (primaryIndex == nullptr)
        {
            ASSERT(secondaryIndices->empty());

            if (baseExpressionType.isArray())
            {
                // If array, assign every element.
                TransformArrayHelper transformHelper(baseExpression);

                TIntermTyped *element      = nullptr;
                TIntermTyped *valueElement = nullptr;
                while ((element = transformHelper.getNextElement(valueExpression, &valueElement)) !=
                       nullptr)
                {
                    TIntermTyped *valueTransposed = CreateTransposeCall(mSymbolTable, valueElement);
                    writeStatements->push_back(
                        new TIntermBinary(EOpAssign, element, valueTransposed));
                }
            }
            else
            {
                TIntermTyped *valueTransposed =
                    CreateTransposeCall(mSymbolTable, valueExpression->deepCopy());
                writeStatements->push_back(
                    new TIntermBinary(assignmentOperator, baseExpression, valueTransposed));
            }

            return;
        }

        // If indexed, create one assignment per secondary index.  If the right-hand side is a
        // scalar, it's used with every assignment.  If it's a vector, the assignment is
        // per-component.  The right-hand side cannot be a matrix as that would imply left-hand
        // side being a matrix too, which is covered above where |primaryIndex == nullptr|.
        ASSERT(!secondaryIndices->empty());

        bool isValueExpressionScalar = valueExpression->getType().getNominalSize() == 1;
        ASSERT(isValueExpressionScalar || valueExpression->getType().getNominalSize() ==
                                              static_cast<int>(secondaryIndices->size()));

        TOperator primaryIndexOp          = GetIndexOp(primaryIndex);
        TIntermTyped *primaryIndexAsTyped = primaryIndex->getAsTyped();

        for (TIntermNode *secondaryIndex : *secondaryIndices)
        {
            TOperator secondaryIndexOp          = GetIndexOp(secondaryIndex);
            TIntermTyped *secondaryIndexAsTyped = secondaryIndex->getAsTyped();

            TIntermBinary *colIndexed = new TIntermBinary(
                secondaryIndexOp, baseExpression->deepCopy(), secondaryIndexAsTyped->deepCopy());
            TIntermBinary *colRowIndexed =
                new TIntermBinary(primaryIndexOp, colIndexed, primaryIndexAsTyped->deepCopy());

            TIntermTyped *valueExpressionIndexed = valueExpression->deepCopy();
            if (!isValueExpressionScalar)
            {
                valueExpressionIndexed = new TIntermBinary(secondaryIndexOp, valueExpressionIndexed,
                                                           secondaryIndexAsTyped->deepCopy());
            }

            writeStatements->push_back(
                new TIntermBinary(assignmentOperator, colRowIndexed, valueExpressionIndexed));
        }
    }

    const TFunction *getCopyStructFieldFunction(const TType *fromFieldType,
                                                const TType *toFieldType,
                                                bool isCopyToOriginal)
    {
        ASSERT(fromFieldType->getStruct());
        ASSERT(toFieldType->getStruct());

        // If copying from or to the original struct, the "to" field struct could require
        // conversion to or from the "from" field struct.  |isCopyToOriginal| tells us if we
        // should expect to find toField or fromField in mStructMapOut, if true or false
        // respectively.
        const TFunction *fieldCopyFunction = nullptr;
        if (isCopyToOriginal)
        {
            const TStructure *toFieldStruct = toFieldType->getStruct();

            auto iter = mStructMapOut->find(toFieldStruct);
            if (iter != mStructMapOut->end())
            {
                declareStructCopyToOriginal(toFieldStruct);
                fieldCopyFunction = iter->second.copyToOriginal;
            }
        }
        else
        {
            const TStructure *fromFieldStruct = fromFieldType->getStruct();

            auto iter = mStructMapOut->find(fromFieldStruct);
            if (iter != mStructMapOut->end())
            {
                declareStructCopyFromOriginal(fromFieldStruct);
                fieldCopyFunction = iter->second.copyFromOriginal;
            }
        }

        return fieldCopyFunction;
    }

    void addFieldCopy(TIntermBlock *body,
                      TIntermTyped *to,
                      TIntermTyped *from,
                      bool isCopyToOriginal)
    {
        const TType &fromType = from->getType();
        const TType &toType   = to->getType();

        TIntermTyped *rhs = from;

        if (fromType.getStruct())
        {
            const TFunction *fieldCopyFunction =
                getCopyStructFieldFunction(&fromType, &toType, isCopyToOriginal);

            if (fieldCopyFunction)
            {
                rhs = CreateStructCopyCall(fieldCopyFunction, from);
            }
        }
        else if (fromType.isMatrix())
        {
            rhs = CreateTransposeCall(mSymbolTable, from);
        }

        body->appendStatement(new TIntermBinary(EOpAssign, to, rhs));
    }

    TFunction *declareStructCopy(const TStructure *from,
                                 const TStructure *to,
                                 bool isCopyToOriginal)
    {
        TType *fromType = new TType(from, true);
        TType *toType   = new TType(to, true);

        // Create the parameter and return value variables.
        TVariable *fromVar = new TVariable(mSymbolTable, ImmutableString("from"), fromType,
                                           SymbolType::AngleInternal);
        TVariable *toVar =
            new TVariable(mSymbolTable, ImmutableString("to"), toType, SymbolType::AngleInternal);

        TIntermSymbol *fromSymbol = new TIntermSymbol(fromVar);
        TIntermSymbol *toSymbol   = new TIntermSymbol(toVar);

        // Create the function body as statements are generated.
        TIntermBlock *body = new TIntermBlock;

        // Declare the result variable.
        TIntermDeclaration *toDecl = new TIntermDeclaration();
        toDecl->appendDeclarator(toSymbol);
        body->appendStatement(toDecl);

        // Iterate over fields of the struct and copy one by one, transposing the matrices.  If a
        // struct is encountered that requires a transformation, this function is recursively
        // called.  As a result, it is important that the copy functions are placed in the code in
        // order.
        const TFieldList &fromFields = from->fields();
        const TFieldList &toFields   = to->fields();
        ASSERT(fromFields.size() == toFields.size());

        for (size_t fieldIndex = 0; fieldIndex < fromFields.size(); ++fieldIndex)
        {
            TIntermTyped *fieldIndexNode = CreateIndexNode(static_cast<int>(fieldIndex));

            TIntermTyped *fromField =
                new TIntermBinary(EOpIndexDirectStruct, fromSymbol->deepCopy(), fieldIndexNode);
            TIntermTyped *toField = new TIntermBinary(EOpIndexDirectStruct, toSymbol->deepCopy(),
                                                      fieldIndexNode->deepCopy());

            const TType *fromFieldType = fromFields[fieldIndex]->type();
            bool isStructOrMatrix      = fromFieldType->getStruct() || fromFieldType->isMatrix();

            if (fromFieldType->isArray() && isStructOrMatrix)
            {
                // If struct or matrix array, we need to copy element by element.
                TransformArrayHelper transformHelper(toField);

                TIntermTyped *toElement   = nullptr;
                TIntermTyped *fromElement = nullptr;
                while ((toElement = transformHelper.getNextElement(fromField, &fromElement)) !=
                       nullptr)
                {
                    addFieldCopy(body, toElement, fromElement, isCopyToOriginal);
                }
            }
            else
            {
                addFieldCopy(body, toField, fromField, isCopyToOriginal);
            }
        }

        // Add return statement.
        body->appendStatement(new TIntermBranch(EOpReturn, toSymbol->deepCopy()));

        // Declare the function
        TFunction *copyFunction = new TFunction(mSymbolTable, kEmptyImmutableString,
                                                SymbolType::AngleInternal, toType, true);
        copyFunction->addParameter(fromVar);

        TIntermFunctionDefinition *functionDef =
            CreateInternalFunctionDefinitionNode(*copyFunction, body);
        mCopyFunctionDefinitionsOut->push_back(functionDef);

        return copyFunction;
    }

    void declareStructCopyFromOriginal(const TStructure *structure)
    {
        StructConversionData *structData = &(*mStructMapOut)[structure];
        if (structData->copyFromOriginal)
        {
            return;
        }

        structData->copyFromOriginal =
            declareStructCopy(structure, structData->convertedStruct, false);
    }

    void declareStructCopyToOriginal(const TStructure *structure)
    {
        StructConversionData *structData = &(*mStructMapOut)[structure];
        if (structData->copyToOriginal)
        {
            return;
        }

        structData->copyToOriginal =
            declareStructCopy(structData->convertedStruct, structure, true);
    }

    TCompiler *mCompiler;

    // This traverser can call itself to transform a subexpression before moving on.  However, it
    // needs to accumulate conversion functions in inner passes.  The fields below marked with Out
    // or In are inherited from the outer pass (for inner passes), or point to storage fields in
    // mOuterPass (for the outer pass).  The latter should not be used by the inner passes as they
    // would be empty, so they are placed inside a struct to make them explicit.
    struct
    {
        StructMap structMap;
        InterfaceBlockMap interfaceBlockMap;
        InterfaceBlockFieldConverted interfaceBlockFieldConverted;
        TIntermSequence copyFunctionDefinitions;
    } mOuterPass;

    // A map from structures with matrices to their converted version.
    StructMap *mStructMapOut;
    // A map from interface block instances with row-major matrices to their converted variable.  If
    // an interface block is nameless, its fields are placed in this map instead.  When a variable
    // in this map is encountered, it signals the start of an expression that my need conversion,
    // which is either "interfaceBlock.field..." or "field..." if nameless.
    InterfaceBlockMap *mInterfaceBlockMap;
    // A map from interface block fields to whether they need to be converted.  If a field was
    // already column-major, it shouldn't be transposed.
    const InterfaceBlockFieldConverted &mInterfaceBlockFieldConvertedIn;

    TIntermSequence *mCopyFunctionDefinitionsOut;

    // If set, it's an inner pass and this will point to the outer pass traverser.  All statement
    // insertions are stored in the outer traverser and applied at once in the end.  This prevents
    // the inner passes from adding statements which invalidates the outer traverser's statement
    // position tracking.
    RewriteRowMajorMatricesTraverser *mOuterTraverser;

    // If set, it's an inner pass that should only process the right-hand side of this particular
    // node.
    TIntermBinary *mInnerPassRoot;
    bool mIsProcessingInnerPassSubtree;
};

}  // anonymous namespace

bool RewriteRowMajorMatrices(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
{
    RewriteRowMajorMatricesTraverser traverser(compiler, symbolTable);
    root->traverse(&traverser);
    if (!traverser.updateTree(compiler, root))
    {
        return false;
    }

    size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
    root->insertChildNodes(firstFunctionIndex, *traverser.getStructCopyFunctions());

    return compiler->validateAST(root);
}
}  // namespace sh
