//
// 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.
//
// Wrapper for Khronos glslang compiler.
//

#include "libANGLE/renderer/glslang_wrapper_utils.h"

// glslang has issues with some specific warnings.
ANGLE_DISABLE_EXTRA_SEMI_WARNING
ANGLE_DISABLE_SHADOWING_WARNING
ANGLE_DISABLE_SUGGEST_OVERRIDE_WARNINGS

// glslang's version of ShaderLang.h, not to be confused with ANGLE's.
#include <glslang/Public/ShaderLang.h>

// Other glslang includes.
#include <SPIRV/GlslangToSpv.h>
#include <StandAlone/ResourceLimits.h>

ANGLE_REENABLE_SUGGEST_OVERRIDE_WARNINGS
ANGLE_REENABLE_SHADOWING_WARNING
ANGLE_REENABLE_EXTRA_SEMI_WARNING

#include <array>
#include <numeric>

#include "common/FixedVector.h"
#include "common/spirv/spirv_instruction_builder_autogen.h"
#include "common/spirv/spirv_instruction_parser_autogen.h"
#include "common/string_utils.h"
#include "common/utilities.h"
#include "libANGLE/Caps.h"
#include "libANGLE/ProgramLinkedResources.h"
#include "libANGLE/trace.h"

#define ANGLE_GLSLANG_CHECK(CALLBACK, TEST, ERR) \
    do                                           \
    {                                            \
        if (ANGLE_UNLIKELY(!(TEST)))             \
        {                                        \
            return CALLBACK(ERR);                \
        }                                        \
                                                 \
    } while (0)

// Enable this for debug logging of pre-transform SPIR-V:
#if !defined(ANGLE_DEBUG_SPIRV_TRANSFORMER)
#    define ANGLE_DEBUG_SPIRV_TRANSFORMER 0
#endif  // !defined(ANGLE_DEBUG_SPIRV_TRANSFORMER)

namespace spirv = angle::spirv;

namespace rx
{
namespace
{
template <size_t N>
constexpr size_t ConstStrLen(const char (&)[N])
{
    static_assert(N > 0, "C++ shouldn't allow N to be zero");

    // The length of a string defined as a char array is the size of the array minus 1 (the
    // terminating '\0').
    return N - 1;
}

void GetBuiltInResourcesFromCaps(const gl::Caps &caps, TBuiltInResource *outBuiltInResources)
{
    outBuiltInResources->maxDrawBuffers                   = caps.maxDrawBuffers;
    outBuiltInResources->maxAtomicCounterBindings         = caps.maxAtomicCounterBufferBindings;
    outBuiltInResources->maxAtomicCounterBufferSize       = caps.maxAtomicCounterBufferSize;
    outBuiltInResources->maxClipPlanes                    = caps.maxClipPlanes;
    outBuiltInResources->maxCombinedAtomicCounterBuffers  = caps.maxCombinedAtomicCounterBuffers;
    outBuiltInResources->maxCombinedAtomicCounters        = caps.maxCombinedAtomicCounters;
    outBuiltInResources->maxCombinedImageUniforms         = caps.maxCombinedImageUniforms;
    outBuiltInResources->maxCombinedTextureImageUnits     = caps.maxCombinedTextureImageUnits;
    outBuiltInResources->maxCombinedShaderOutputResources = caps.maxCombinedShaderOutputResources;
    outBuiltInResources->maxComputeWorkGroupCountX        = caps.maxComputeWorkGroupCount[0];
    outBuiltInResources->maxComputeWorkGroupCountY        = caps.maxComputeWorkGroupCount[1];
    outBuiltInResources->maxComputeWorkGroupCountZ        = caps.maxComputeWorkGroupCount[2];
    outBuiltInResources->maxComputeWorkGroupSizeX         = caps.maxComputeWorkGroupSize[0];
    outBuiltInResources->maxComputeWorkGroupSizeY         = caps.maxComputeWorkGroupSize[1];
    outBuiltInResources->maxComputeWorkGroupSizeZ         = caps.maxComputeWorkGroupSize[2];
    outBuiltInResources->minProgramTexelOffset            = caps.minProgramTexelOffset;
    outBuiltInResources->maxFragmentUniformVectors        = caps.maxFragmentUniformVectors;
    outBuiltInResources->maxFragmentInputComponents       = caps.maxFragmentInputComponents;
    outBuiltInResources->maxGeometryInputComponents       = caps.maxGeometryInputComponents;
    outBuiltInResources->maxGeometryOutputComponents      = caps.maxGeometryOutputComponents;
    outBuiltInResources->maxGeometryOutputVertices        = caps.maxGeometryOutputVertices;
    outBuiltInResources->maxGeometryTotalOutputComponents = caps.maxGeometryTotalOutputComponents;
    outBuiltInResources->maxPatchVertices                 = caps.maxPatchVertices;
    outBuiltInResources->maxLights                        = caps.maxLights;
    outBuiltInResources->maxProgramTexelOffset            = caps.maxProgramTexelOffset;
    outBuiltInResources->maxVaryingComponents             = caps.maxVaryingComponents;
    outBuiltInResources->maxVaryingVectors                = caps.maxVaryingVectors;
    outBuiltInResources->maxVertexAttribs                 = caps.maxVertexAttributes;
    outBuiltInResources->maxVertexOutputComponents        = caps.maxVertexOutputComponents;
    outBuiltInResources->maxVertexUniformVectors          = caps.maxVertexUniformVectors;
    outBuiltInResources->maxClipDistances                 = caps.maxClipDistances;
    outBuiltInResources->maxSamples                       = caps.maxSamples;
    outBuiltInResources->maxCullDistances                 = caps.maxCullDistances;
    outBuiltInResources->maxCombinedClipAndCullDistances  = caps.maxCombinedClipAndCullDistances;
}

// Run at startup to warm up glslang's internals to avoid hitches on first shader compile.
void GlslangWarmup()
{
    ANGLE_TRACE_EVENT0("gpu.angle,startup", "GlslangWarmup");

    EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
    // EShMessages messages = EShMsgDefault;

    const TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
    glslang::TShader warmUpShader(EShLangVertex);

    const char *kShaderString = R"(#version 450 core
        void main(){}
    )";
    const int kShaderLength   = static_cast<int>(strlen(kShaderString));

    warmUpShader.setStringsWithLengths(&kShaderString, &kShaderLength, 1);
    warmUpShader.setEntryPoint("main");

    bool result = warmUpShader.parse(&builtInResources, 450, ECoreProfile, false, false, messages);
    ASSERT(result);
}

bool IsRotationIdentity(SurfaceRotation rotation)
{
    return rotation == SurfaceRotation::Identity || rotation == SurfaceRotation::FlippedIdentity;
}

// Test if there are non-zero indices in the uniform name, returning false in that case.  This
// happens for multi-dimensional arrays, where a uniform is created for every possible index of the
// array (except for the innermost dimension).  When assigning decorations (set/binding/etc), only
// the indices corresponding to the first element of the array should be specified.  This function
// is used to skip the other indices.
bool UniformNameIsIndexZero(const std::string &name)
{
    size_t lastBracketClose = 0;

    while (true)
    {
        size_t openBracket = name.find('[', lastBracketClose);
        if (openBracket == std::string::npos)
        {
            break;
        }
        size_t closeBracket = name.find(']', openBracket);

        // If the index between the brackets is not zero, ignore this uniform.
        if (name.substr(openBracket + 1, closeBracket - openBracket - 1) != "0")
        {
            return false;
        }
        lastBracketClose = closeBracket;
    }

    return true;
}

bool MappedSamplerNameNeedsUserDefinedPrefix(const std::string &originalName)
{
    return originalName.find('.') == std::string::npos;
}

template <typename OutputIter, typename ImplicitIter>
uint32_t CountExplicitOutputs(OutputIter outputsBegin,
                              OutputIter outputsEnd,
                              ImplicitIter implicitsBegin,
                              ImplicitIter implicitsEnd)
{
    auto reduce = [implicitsBegin, implicitsEnd](uint32_t count, const sh::ShaderVariable &var) {
        bool isExplicit = std::find(implicitsBegin, implicitsEnd, var.name) == implicitsEnd;
        return count + isExplicit;
    };

    return std::accumulate(outputsBegin, outputsEnd, 0, reduce);
}

ShaderInterfaceVariableInfo *AddResourceInfoToAllStages(ShaderInterfaceVariableInfoMap *infoMap,
                                                        gl::ShaderType shaderType,
                                                        const std::string &varName,
                                                        uint32_t descriptorSet,
                                                        uint32_t binding)
{
    gl::ShaderBitSet allStages;
    allStages.set();

    ShaderInterfaceVariableInfo &info = infoMap->add(shaderType, varName);
    info.descriptorSet                = descriptorSet;
    info.binding                      = binding;
    info.activeStages                 = allStages;
    return &info;
}

ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap,
                                             gl::ShaderType shaderType,
                                             const std::string &varName,
                                             uint32_t descriptorSet,
                                             uint32_t binding)
{
    gl::ShaderBitSet stages;
    stages.set(shaderType);

    ShaderInterfaceVariableInfo &info = infoMap->add(shaderType, varName);
    info.descriptorSet                = descriptorSet;
    info.binding                      = binding;
    info.activeStages                 = stages;
    return &info;
}

// Add location information for an in/out variable.
ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
                                             gl::ShaderType shaderType,
                                             const std::string &varName,
                                             uint32_t location,
                                             uint32_t component,
                                             uint8_t attributeComponentCount,
                                             uint8_t attributeLocationCount)
{
    // The info map for this name may or may not exist already.  This function merges the
    // location/component information.
    ShaderInterfaceVariableInfo &info = infoMap->addOrGet(shaderType, varName);

    ASSERT(info.descriptorSet == ShaderInterfaceVariableInfo::kInvalid);
    ASSERT(info.binding == ShaderInterfaceVariableInfo::kInvalid);
    ASSERT(info.location == ShaderInterfaceVariableInfo::kInvalid);
    ASSERT(info.component == ShaderInterfaceVariableInfo::kInvalid);

    info.location  = location;
    info.component = component;
    info.activeStages.set(shaderType);
    info.attributeComponentCount = attributeComponentCount;
    info.attributeLocationCount  = attributeLocationCount;

    return &info;
}

// Add location information for an in/out variable
void AddVaryingLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
                            const gl::VaryingInShaderRef &ref,
                            const bool isStructField,
                            const uint32_t location,
                            const uint32_t component)
{
    const std::string &name = isStructField ? ref.parentStructMappedName : ref.varying->mappedName;
    AddLocationInfo(infoMap, ref.stage, name, location, component, 0, 0);
}

// Modify an existing out variable and add transform feedback information.
ShaderInterfaceVariableInfo *SetXfbInfo(ShaderInterfaceVariableInfoMap *infoMap,
                                        gl::ShaderType shaderType,
                                        const std::string &varName,
                                        int fieldIndex,
                                        uint32_t xfbBuffer,
                                        uint32_t xfbOffset,
                                        uint32_t xfbStride,
                                        uint32_t arraySize,
                                        uint32_t columnCount,
                                        uint32_t rowCount,
                                        uint32_t arrayIndex,
                                        GLenum componentType)
{
    ShaderInterfaceVariableInfo &info   = infoMap->get(shaderType, varName);
    ShaderInterfaceVariableXfbInfo *xfb = &info.xfb;

    if (fieldIndex >= 0)
    {
        if (info.fieldXfb.size() <= static_cast<size_t>(fieldIndex))
        {
            info.fieldXfb.resize(fieldIndex + 1);
        }
        xfb = &info.fieldXfb[fieldIndex];
    }

    ASSERT(xfb->buffer == ShaderInterfaceVariableXfbInfo::kInvalid);
    ASSERT(xfb->offset == ShaderInterfaceVariableXfbInfo::kInvalid);
    ASSERT(xfb->stride == ShaderInterfaceVariableXfbInfo::kInvalid);

    if (arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid)
    {
        xfb->arrayElements.emplace_back();
        xfb = &xfb->arrayElements.back();
    }

    xfb->buffer        = xfbBuffer;
    xfb->offset        = xfbOffset;
    xfb->stride        = xfbStride;
    xfb->arraySize     = arraySize;
    xfb->columnCount   = columnCount;
    xfb->rowCount      = rowCount;
    xfb->arrayIndex    = arrayIndex;
    xfb->componentType = componentType;

    return &info;
}

void AssignTransformFeedbackEmulationBindings(gl::ShaderType shaderType,
                                              const gl::ProgramState &programState,
                                              bool isTransformFeedbackStage,
                                              GlslangProgramInterfaceInfo *programInterfaceInfo,
                                              ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    size_t bufferCount = 0;
    if (isTransformFeedbackStage)
    {
        ASSERT(!programState.getLinkedTransformFeedbackVaryings().empty());
        const bool isInterleaved =
            programState.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
        bufferCount = isInterleaved ? 1 : programState.getLinkedTransformFeedbackVaryings().size();
    }

    // Add entries for the transform feedback buffers to the info map, so they can have correct
    // set/binding.
    for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
    {
        AddResourceInfo(variableInfoMapOut, shaderType, GetXfbBufferName(bufferIndex),
                        programInterfaceInfo->uniformsAndXfbDescriptorSetIndex,
                        programInterfaceInfo->currentUniformBindingIndex);
        ++programInterfaceInfo->currentUniformBindingIndex;
    }

    // Remove inactive transform feedback buffers.
    for (uint32_t bufferIndex = bufferCount;
         bufferIndex < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; ++bufferIndex)
    {
        variableInfoMapOut->add(shaderType, GetXfbBufferName(bufferIndex));
    }
}

void AssignTransformFeedbackExtensionLocations(gl::ShaderType shaderType,
                                               const gl::ProgramState &programState,
                                               bool isTransformFeedbackStage,
                                               GlslangProgramInterfaceInfo *programInterfaceInfo,
                                               ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    // The only varying that requires additional resources is gl_Position, as it's indirectly
    // captured through ANGLEXfbPosition.

    const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
        programState.getLinkedTransformFeedbackVaryings();

    bool capturesPosition = false;

    if (isTransformFeedbackStage)
    {
        for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
        {
            const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
            const std::string &tfVaryingName              = tfVarying.mappedName;

            if (tfVaryingName == "gl_Position")
            {
                ASSERT(tfVarying.isBuiltIn());
                capturesPosition = true;
                break;
            }
        }
    }

    if (capturesPosition)
    {
        AddLocationInfo(variableInfoMapOut, shaderType, sh::vk::kXfbExtensionPositionOutName,
                        programInterfaceInfo->locationsUsedForXfbExtension, 0, 0, 0);
        ++programInterfaceInfo->locationsUsedForXfbExtension;
    }
    else
    {
        // Make sure this varying is removed from the other stages, or if position is not captured
        // at all.
        variableInfoMapOut->add(shaderType, sh::vk::kXfbExtensionPositionOutName);
    }
}

bool IsFirstRegisterOfVarying(const gl::PackedVaryingRegister &varyingReg, bool allowFields)
{
    const gl::PackedVarying &varying = *varyingReg.packedVarying;

    // In Vulkan GLSL, struct fields are not allowed to have location assignments.  The varying of a
    // struct type is thus given a location equal to the one assigned to its first field.  With I/O
    // blocks, transform feedback can capture an arbitrary field.  In that case, we need to look at
    // every field, not just the first one.
    if (!allowFields && varying.isStructField() &&
        (varying.fieldIndex > 0 || varying.secondaryFieldIndex > 0))
    {
        return false;
    }

    // Similarly, assign array varying locations to the assigned location of the first element.
    if (varyingReg.varyingArrayIndex != 0 ||
        (varying.arrayIndex != GL_INVALID_INDEX && varying.arrayIndex != 0))
    {
        return false;
    }

    // Similarly, assign matrix varying locations to the assigned location of the first row.
    if (varyingReg.varyingRowIndex != 0)
    {
        return false;
    }

    return true;
}

void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable,
                              gl::ShaderType shaderType,
                              ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    // Assign attribute locations for the vertex shader.
    for (const sh::ShaderVariable &attribute : programExecutable.getProgramInputs())
    {
        ASSERT(attribute.active);

        const uint8_t colCount = static_cast<uint8_t>(gl::VariableColumnCount(attribute.type));
        const uint8_t rowCount = static_cast<uint8_t>(gl::VariableRowCount(attribute.type));
        const bool isMatrix    = colCount > 1 && rowCount > 1;

        const uint8_t componentCount = isMatrix ? rowCount : colCount;
        const uint8_t locationCount  = isMatrix ? colCount : rowCount;

        AddLocationInfo(variableInfoMapOut, shaderType, attribute.mappedName, attribute.location,
                        ShaderInterfaceVariableInfo::kInvalid, componentCount, locationCount);
    }
}

void AssignSecondaryOutputLocations(const gl::ProgramState &programState,
                                    ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    const auto &secondaryOutputLocations =
        programState.getExecutable().getSecondaryOutputLocations();
    const auto &outputVariables = programState.getExecutable().getOutputVariables();

    // Handle EXT_blend_func_extended secondary outputs (ones with index=1)
    for (const gl::VariableLocation &outputLocation : secondaryOutputLocations)
    {
        if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
        {
            const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index];

            uint32_t location = 0;
            if (outputVar.location != -1)
            {
                location = outputVar.location;
            }

            ShaderInterfaceVariableInfo *info =
                AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment, outputVar.mappedName,
                                location, ShaderInterfaceVariableInfo::kInvalid, 0, 0);

            // If the shader source has not specified the index, specify it here.
            if (outputVar.index == -1)
            {
                // Index 1 is used to specify that the color be used as the second color input to
                // the blend equation
                info->index = 1;
            }
        }
    }
    // Handle secondary outputs for ESSL version less than 3.00
    gl::Shader *fragmentShader = programState.getAttachedShader(gl::ShaderType::Fragment);
    if (fragmentShader && fragmentShader->getShaderVersion() == 100)
    {
        const auto &shaderOutputs = fragmentShader->getActiveOutputVariables();
        for (const auto &outputVar : shaderOutputs)
        {
            if (outputVar.name == "gl_SecondaryFragColorEXT")
            {
                AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment,
                                "angle_SecondaryFragColor", 0,
                                ShaderInterfaceVariableInfo::kInvalid, 0, 0);
            }
            else if (outputVar.name == "gl_SecondaryFragDataEXT")
            {
                AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment,
                                "angle_SecondaryFragData", 0, ShaderInterfaceVariableInfo::kInvalid,
                                0, 0);
            }
        }
    }
}

void AssignOutputLocations(const gl::ProgramState &programState,
                           const gl::ShaderType shaderType,
                           ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    // Assign output locations for the fragment shader.
    ASSERT(shaderType == gl::ShaderType::Fragment);

    const gl::ProgramExecutable &programExecutable   = programState.getExecutable();
    const auto &outputLocations                      = programExecutable.getOutputLocations();
    const auto &outputVariables                      = programExecutable.getOutputVariables();
    const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask",
                                                        "gl_FragStencilRefARB"};

    for (const gl::VariableLocation &outputLocation : outputLocations)
    {
        if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
        {
            const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index];

            uint32_t location = 0;
            if (outputVar.location != -1)
            {
                location = outputVar.location;
            }
            else if (std::find(implicitOutputs.begin(), implicitOutputs.end(), outputVar.name) ==
                     implicitOutputs.end())
            {
                // If there is only one output, it is allowed not to have a location qualifier, in
                // which case it defaults to 0.  GLSL ES 3.00 spec, section 4.3.8.2.
                ASSERT(CountExplicitOutputs(outputVariables.begin(), outputVariables.end(),
                                            implicitOutputs.begin(), implicitOutputs.end()) == 1);
            }

            AddLocationInfo(variableInfoMapOut, shaderType, outputVar.mappedName, location,
                            ShaderInterfaceVariableInfo::kInvalid, 0, 0);
        }
    }

    AssignSecondaryOutputLocations(programState, variableInfoMapOut);

    // When no fragment output is specified by the shader, the translator outputs webgl_FragColor or
    // webgl_FragData.  Add an entry for these.  Even though the translator is already assigning
    // location 0 to these entries, adding an entry for them here allows us to ASSERT that every
    // shader interface variable is processed during the SPIR-V transformation.  This is done when
    // iterating the ids provided by OpEntryPoint.
    AddLocationInfo(variableInfoMapOut, shaderType, "webgl_FragColor", 0, 0, 0, 0);
    AddLocationInfo(variableInfoMapOut, shaderType, "webgl_FragData", 0, 0, 0, 0);
}

void AssignVaryingLocations(const GlslangSourceOptions &options,
                            const gl::VaryingPacking &varyingPacking,
                            const gl::ShaderType shaderType,
                            const gl::ShaderType frontShaderType,
                            GlslangProgramInterfaceInfo *programInterfaceInfo,
                            ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    uint32_t locationsUsedForEmulation = programInterfaceInfo->locationsUsedForXfbExtension;

    // Substitute layout and qualifier strings for the position varying added for line raster
    // emulation.
    if (options.emulateBresenhamLines)
    {
        uint32_t lineRasterEmulationPositionLocation = locationsUsedForEmulation++;

        AddLocationInfo(variableInfoMapOut, shaderType, sh::vk::kLineRasterEmulationPosition,
                        lineRasterEmulationPositionLocation, ShaderInterfaceVariableInfo::kInvalid,
                        0, 0);
    }

    // Assign varying locations.
    for (const gl::PackedVaryingRegister &varyingReg : varyingPacking.getRegisterList())
    {
        if (!IsFirstRegisterOfVarying(varyingReg, false))
        {
            continue;
        }

        const gl::PackedVarying &varying = *varyingReg.packedVarying;

        uint32_t location  = varyingReg.registerRow + locationsUsedForEmulation;
        uint32_t component = ShaderInterfaceVariableInfo::kInvalid;
        if (varyingReg.registerColumn > 0)
        {
            ASSERT(!varying.varying().isStruct());
            ASSERT(!gl::IsMatrixType(varying.varying().type));
            component = varyingReg.registerColumn;
        }

        // In the following:
        //
        //     struct S { vec4 field; };
        //     out S varStruct;
        //
        // "_uvarStruct" is found through |parentStructMappedName|, with |varying->mappedName|
        // being "_ufield".  In such a case, use |parentStructMappedName|.
        if (varying.frontVarying.varying && (varying.frontVarying.stage == shaderType))
        {
            AddVaryingLocationInfo(variableInfoMapOut, varying.frontVarying,
                                   varying.isStructField(), location, component);
        }

        if (varying.backVarying.varying && (varying.backVarying.stage == shaderType))
        {
            AddVaryingLocationInfo(variableInfoMapOut, varying.backVarying, varying.isStructField(),
                                   location, component);
        }
    }

    // Add an entry for inactive varyings.
    const gl::ShaderMap<std::vector<std::string>> &inactiveVaryingMappedNames =
        varyingPacking.getInactiveVaryingMappedNames();
    for (const std::string &varyingName : inactiveVaryingMappedNames[shaderType])
    {
        ASSERT(!gl::IsBuiltInName(varyingName));

        // If name is already in the map, it will automatically have marked all other stages
        // inactive.
        if (variableInfoMapOut->contains(shaderType, varyingName))
        {
            continue;
        }

        // Otherwise, add an entry for it with all locations inactive.
        ShaderInterfaceVariableInfo &info = variableInfoMapOut->addOrGet(shaderType, varyingName);
        ASSERT(info.location == ShaderInterfaceVariableInfo::kInvalid);
    }

    // Add an entry for active builtins varyings.  This will allow inactive builtins, such as
    // gl_PointSize, gl_ClipDistance etc to be removed.
    const gl::ShaderMap<std::vector<std::string>> &activeOutputBuiltIns =
        varyingPacking.getActiveOutputBuiltInNames();
    for (const std::string &builtInName : activeOutputBuiltIns[shaderType])
    {
        ASSERT(gl::IsBuiltInName(builtInName));

        ShaderInterfaceVariableInfo &info = variableInfoMapOut->addOrGet(shaderType, builtInName);
        info.activeStages.set(shaderType);
        info.varyingIsOutput = true;
    }

    // If an output builtin is active in the previous stage, assume it's active in the input of the
    // current stage as well.
    if (frontShaderType != gl::ShaderType::InvalidEnum)
    {
        for (const std::string &builtInName : activeOutputBuiltIns[frontShaderType])
        {
            ASSERT(gl::IsBuiltInName(builtInName));

            ShaderInterfaceVariableInfo &info =
                variableInfoMapOut->addOrGet(shaderType, builtInName);
            info.activeStages.set(shaderType);
            info.varyingIsInput = true;
        }
    }

    // Add an entry for gl_PerVertex, for use with transform feedback capture of built-ins.
    ShaderInterfaceVariableInfo &info = variableInfoMapOut->addOrGet(shaderType, "gl_PerVertex");
    info.activeStages.set(shaderType);
}

// Calculates XFB layout qualifier arguments for each tranform feedback varying.  Stores calculated
// values for the SPIR-V transformation.
void AssignTransformFeedbackQualifiers(const gl::ProgramExecutable &programExecutable,
                                       const gl::VaryingPacking &varyingPacking,
                                       const gl::ShaderType shaderType,
                                       bool usesExtension,
                                       ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
        programExecutable.getLinkedTransformFeedbackVaryings();
    const std::vector<GLsizei> &varyingStrides = programExecutable.getTransformFeedbackStrides();
    const bool isInterleaved =
        programExecutable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;

    uint32_t currentOffset = 0;
    uint32_t currentStride = 0;
    uint32_t bufferIndex   = 0;

    for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
    {
        if (isInterleaved)
        {
            bufferIndex = 0;
            if (varyingIndex > 0)
            {
                const gl::TransformFeedbackVarying &prev = tfVaryings[varyingIndex - 1];
                currentOffset += prev.size() * gl::VariableExternalSize(prev.type);
            }
            currentStride = varyingStrides[0];
        }
        else
        {
            bufferIndex   = varyingIndex;
            currentOffset = 0;
            currentStride = varyingStrides[varyingIndex];
        }

        const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
        const gl::UniformTypeInfo &uniformInfo        = gl::GetUniformTypeInfo(tfVarying.type);
        const uint32_t varyingSize =
            tfVarying.isArray() ? tfVarying.size() : ShaderInterfaceVariableXfbInfo::kInvalid;

        if (tfVarying.isBuiltIn())
        {
            if (usesExtension && tfVarying.name == "gl_Position")
            {
                // With the extension, gl_Position is captured via a special varying.
                SetXfbInfo(variableInfoMapOut, shaderType, sh::vk::kXfbExtensionPositionOutName, -1,
                           bufferIndex, currentOffset, currentStride, varyingSize,
                           uniformInfo.columnCount, uniformInfo.rowCount,
                           ShaderInterfaceVariableXfbInfo::kInvalid, uniformInfo.componentType);
            }
            else
            {
                // gl_PerVertex is always defined as:
                //
                //    Field 0: gl_Position
                //    Field 1: gl_PointSize
                //    Field 2: gl_ClipDistance
                //    Field 3: gl_CullDistance
                //
                // With the extension, all fields except gl_Position can be captured directly by
                // decorating gl_PerVertex fields.
                int fieldIndex                                                              = -1;
                constexpr int kPerVertexMemberCount                                         = 4;
                constexpr std::array<const char *, kPerVertexMemberCount> kPerVertexMembers = {
                    "gl_Position",
                    "gl_PointSize",
                    "gl_ClipDistance",
                    "gl_CullDistance",
                };
                for (int index = 0; index < kPerVertexMemberCount; ++index)
                {
                    if (tfVarying.name == kPerVertexMembers[index])
                    {
                        fieldIndex = index;
                        break;
                    }
                }
                ASSERT(fieldIndex != -1);
                ASSERT(!usesExtension || fieldIndex > 0);

                SetXfbInfo(variableInfoMapOut, shaderType, "gl_PerVertex", fieldIndex, bufferIndex,
                           currentOffset, currentStride, varyingSize, uniformInfo.columnCount,
                           uniformInfo.rowCount, ShaderInterfaceVariableXfbInfo::kInvalid,
                           uniformInfo.componentType);
            }

            continue;
        }
        // Note: capturing individual array elements using the Vulkan transform feedback extension
        // is currently not supported due to limitations in the extension.
        // ANGLE supports capturing the whole array.
        // http://anglebug.com/4140
        if (usesExtension && tfVarying.isArray() && tfVarying.arrayIndex != GL_INVALID_INDEX)
        {
            continue;
        }

        // Find the varying with this name.  If a struct is captured, we would be iterating over its
        // fields, and the name of the varying is found through parentStructMappedName.  This should
        // only be done for the first field of the struct.  For I/O blocks on the other hand, we
        // need to decorate the exact member that is captured (as whole-block capture is not
        // supported).
        const gl::PackedVarying *originalVarying = nullptr;
        for (const gl::PackedVaryingRegister &varyingReg : varyingPacking.getRegisterList())
        {
            if (!IsFirstRegisterOfVarying(varyingReg, tfVarying.isShaderIOBlock))
            {
                continue;
            }

            const gl::PackedVarying *varying = varyingReg.packedVarying;

            if (tfVarying.isShaderIOBlock)
            {
                if (varying->frontVarying.parentStructName == tfVarying.structOrBlockName)
                {
                    size_t pos = tfVarying.name.find_first_of(".");
                    std::string fieldName =
                        pos == std::string::npos ? tfVarying.name : tfVarying.name.substr(pos + 1);

                    if (fieldName == varying->frontVarying.varying->name.c_str())
                    {
                        originalVarying = varying;
                        break;
                    }
                }
            }
            else if (varying->frontVarying.varying->name == tfVarying.name)
            {
                originalVarying = varying;
                break;
            }
        }

        if (originalVarying)
        {
            const std::string &mappedName =
                originalVarying->isStructField()
                    ? originalVarying->frontVarying.parentStructMappedName
                    : originalVarying->frontVarying.varying->mappedName;

            const int fieldIndex = tfVarying.isShaderIOBlock ? originalVarying->fieldIndex : -1;
            const uint32_t arrayIndex = tfVarying.arrayIndex == GL_INVALID_INDEX
                                            ? ShaderInterfaceVariableXfbInfo::kInvalid
                                            : tfVarying.arrayIndex;

            // Set xfb info for this varying.  AssignVaryingLocations should have already added
            // location information for these varyings.
            SetXfbInfo(variableInfoMapOut, shaderType, mappedName, fieldIndex, bufferIndex,
                       currentOffset, currentStride, varyingSize, uniformInfo.columnCount,
                       uniformInfo.rowCount, arrayIndex, uniformInfo.componentType);
        }
    }
}

void AssignUniformBindings(const GlslangSourceOptions &options,
                           const gl::ProgramExecutable &programExecutable,
                           const gl::ShaderType shaderType,
                           GlslangProgramInterfaceInfo *programInterfaceInfo,
                           ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    if (programExecutable.hasLinkedShaderStage(shaderType))
    {
        AddResourceInfo(variableInfoMapOut, shaderType, kDefaultUniformNames[shaderType],
                        programInterfaceInfo->uniformsAndXfbDescriptorSetIndex,
                        programInterfaceInfo->currentUniformBindingIndex);
        ++programInterfaceInfo->currentUniformBindingIndex;

        // Assign binding to the driver uniforms block
        AddResourceInfoToAllStages(variableInfoMapOut, shaderType, sh::vk::kDriverUniformsBlockName,
                                   programInterfaceInfo->driverUniformsDescriptorSetIndex, 0);
    }
}

// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignInputAttachmentBindings(const GlslangSourceOptions &options,
                                   const gl::ProgramExecutable &programExecutable,
                                   const std::vector<gl::LinkedUniform> &uniforms,
                                   const gl::RangeUI &inputAttachmentUniformRange,
                                   const gl::ShaderType shaderType,
                                   GlslangProgramInterfaceInfo *programInterfaceInfo,
                                   ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    const uint32_t baseInputAttachmentBindingIndex =
        programInterfaceInfo->currentShaderResourceBindingIndex;

    bool hasFragmentInOutVars = false;

    for (unsigned int uniformIndex : inputAttachmentUniformRange)
    {
        std::string mappedInputAttachmentName;
        const gl::LinkedUniform &inputAttachmentUniform = uniforms[uniformIndex];
        mappedInputAttachmentName                       = inputAttachmentUniform.mappedName;

        if (programExecutable.hasLinkedShaderStage(shaderType) &&
            inputAttachmentUniform.isActive(shaderType))
        {
            const uint32_t inputAttachmentBindingIndex =
                baseInputAttachmentBindingIndex + inputAttachmentUniform.location;

            AddResourceInfo(variableInfoMapOut, shaderType, mappedInputAttachmentName,
                            programInterfaceInfo->shaderResourceDescriptorSetIndex,
                            inputAttachmentBindingIndex);

            hasFragmentInOutVars = true;
        }
    }

    if (hasFragmentInOutVars)
    {
        // For input attachment uniform, the descriptor set binding indices are allocated as much as
        // the maximum draw buffers.
        programInterfaceInfo->currentShaderResourceBindingIndex +=
            gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
    }
}

// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignInterfaceBlockBindings(const GlslangSourceOptions &options,
                                  const gl::ProgramExecutable &programExecutable,
                                  const std::vector<gl::InterfaceBlock> &blocks,
                                  const gl::ShaderType shaderType,
                                  GlslangProgramInterfaceInfo *programInterfaceInfo,
                                  ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    for (const gl::InterfaceBlock &block : blocks)
    {
        if (!block.isArray || block.arrayElement == 0)
        {
            // TODO: http://anglebug.com/4523: All blocks should be active
            if (programExecutable.hasLinkedShaderStage(shaderType) && block.isActive(shaderType))
            {
                AddResourceInfo(variableInfoMapOut, shaderType, block.mappedName,
                                programInterfaceInfo->shaderResourceDescriptorSetIndex,
                                programInterfaceInfo->currentShaderResourceBindingIndex);
                ++programInterfaceInfo->currentShaderResourceBindingIndex;
            }
        }
    }
}

// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignAtomicCounterBufferBindings(const GlslangSourceOptions &options,
                                       const gl::ProgramExecutable &programExecutable,
                                       const std::vector<gl::AtomicCounterBuffer> &buffers,
                                       const gl::ShaderType shaderType,
                                       GlslangProgramInterfaceInfo *programInterfaceInfo,
                                       ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    if (buffers.size() == 0)
    {
        return;
    }

    if (programExecutable.hasLinkedShaderStage(shaderType))
    {
        AddResourceInfo(variableInfoMapOut, shaderType, sh::vk::kAtomicCountersBlockName,
                        programInterfaceInfo->shaderResourceDescriptorSetIndex,
                        programInterfaceInfo->currentShaderResourceBindingIndex);
        ++programInterfaceInfo->currentShaderResourceBindingIndex;
    }
}

// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignImageBindings(const GlslangSourceOptions &options,
                         const gl::ProgramExecutable &programExecutable,
                         const std::vector<gl::LinkedUniform> &uniforms,
                         const gl::RangeUI &imageUniformRange,
                         const gl::ShaderType shaderType,
                         GlslangProgramInterfaceInfo *programInterfaceInfo,
                         ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    for (unsigned int uniformIndex : imageUniformRange)
    {
        const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];

        std::string name = imageUniform.mappedName;
        if (GetImageNameWithoutIndices(&name))
        {
            if (programExecutable.hasLinkedShaderStage(shaderType))
            {
                AddResourceInfo(variableInfoMapOut, shaderType, name,
                                programInterfaceInfo->shaderResourceDescriptorSetIndex,
                                programInterfaceInfo->currentShaderResourceBindingIndex);
                ++programInterfaceInfo->currentShaderResourceBindingIndex;
            }
        }
    }
}

void AssignNonTextureBindings(const GlslangSourceOptions &options,
                              const gl::ProgramExecutable &programExecutable,
                              const gl::ShaderType shaderType,
                              GlslangProgramInterfaceInfo *programInterfaceInfo,
                              ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    const std::vector<gl::LinkedUniform> &uniforms = programExecutable.getUniforms();
    const gl::RangeUI &inputAttachmentUniformRange = programExecutable.getFragmentInoutRange();
    AssignInputAttachmentBindings(options, programExecutable, uniforms, inputAttachmentUniformRange,
                                  shaderType, programInterfaceInfo, variableInfoMapOut);

    const std::vector<gl::InterfaceBlock> &uniformBlocks = programExecutable.getUniformBlocks();
    AssignInterfaceBlockBindings(options, programExecutable, uniformBlocks, shaderType,
                                 programInterfaceInfo, variableInfoMapOut);

    const std::vector<gl::InterfaceBlock> &storageBlocks =
        programExecutable.getShaderStorageBlocks();
    AssignInterfaceBlockBindings(options, programExecutable, storageBlocks, shaderType,
                                 programInterfaceInfo, variableInfoMapOut);

    const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
        programExecutable.getAtomicCounterBuffers();
    AssignAtomicCounterBufferBindings(options, programExecutable, atomicCounterBuffers, shaderType,
                                      programInterfaceInfo, variableInfoMapOut);

    const gl::RangeUI &imageUniformRange = programExecutable.getImageUniformRange();
    AssignImageBindings(options, programExecutable, uniforms, imageUniformRange, shaderType,
                        programInterfaceInfo, variableInfoMapOut);
}

// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignTextureBindings(const GlslangSourceOptions &options,
                           const gl::ProgramExecutable &programExecutable,
                           const gl::ShaderType shaderType,
                           GlslangProgramInterfaceInfo *programInterfaceInfo,
                           ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    // Assign textures to a descriptor set and binding.
    const std::vector<gl::LinkedUniform> &uniforms = programExecutable.getUniforms();

    for (unsigned int uniformIndex : programExecutable.getSamplerUniformRange())
    {
        const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];

        if (gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
        {
            continue;
        }

        if (UniformNameIsIndexZero(samplerUniform.name))
        {
            // Samplers in structs are extracted and renamed.
            const std::string samplerName = GlslangGetMappedSamplerName(samplerUniform.name);

            // TODO: http://anglebug.com/4523: All uniforms should be active
            if (programExecutable.hasLinkedShaderStage(shaderType) &&
                samplerUniform.isActive(shaderType))
            {
                AddResourceInfo(variableInfoMapOut, shaderType, samplerName,
                                programInterfaceInfo->textureDescriptorSetIndex,
                                programInterfaceInfo->currentTextureBindingIndex);
                ++programInterfaceInfo->currentTextureBindingIndex;
            }
        }
    }
}

constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
    {gl::ShaderType::Vertex, EShLangVertex},
    {gl::ShaderType::TessControl, EShLangTessControl},
    {gl::ShaderType::TessEvaluation, EShLangTessEvaluation},
    {gl::ShaderType::Geometry, EShLangGeometry},
    {gl::ShaderType::Fragment, EShLangFragment},
    {gl::ShaderType::Compute, EShLangCompute},
};

angle::Result CompileShader(const GlslangErrorCallback &callback,
                            const TBuiltInResource &builtInResources,
                            gl::ShaderType shaderType,
                            const std::string &shaderSource,
                            glslang::TShader *shader,
                            glslang::TProgram *program)
{
    // Enable SPIR-V and Vulkan rules when parsing GLSL
    constexpr EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);

    ANGLE_TRACE_EVENT0("gpu.angle", "Glslang CompileShader TShader::parse");

    const char *shaderString = shaderSource.c_str();
    int shaderLength         = static_cast<int>(shaderSource.size());

    shader->setStringsWithLengths(&shaderString, &shaderLength, 1);
    shader->setEntryPoint("main");

    bool result = shader->parse(&builtInResources, 450, ECoreProfile, false, false, messages);
    if (!result)
    {
        ERR() << "Internal error parsing Vulkan shader corresponding to " << shaderType << ":\n"
              << shader->getInfoLog() << "\n"
              << shader->getInfoDebugLog() << "\n";
        ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
    }

    program->addShader(shader);

    return angle::Result::Continue;
}

angle::Result LinkProgram(const GlslangErrorCallback &callback, glslang::TProgram *program)
{
    // Enable SPIR-V and Vulkan rules
    constexpr EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);

    bool linkResult = program->link(messages);
    if (!linkResult)
    {
        ERR() << "Internal error linking Vulkan shaders:\n" << program->getInfoLog() << "\n";
        ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
    }
    return angle::Result::Continue;
}

// Base class for SPIR-V transformations.
class SpirvTransformerBase : angle::NonCopyable
{
  public:
    SpirvTransformerBase(const spirv::Blob &spirvBlobIn,
                         const ShaderInterfaceVariableInfoMap &variableInfoMap,
                         spirv::Blob *spirvBlobOut)
        : mSpirvBlobIn(spirvBlobIn), mVariableInfoMap(variableInfoMap), mSpirvBlobOut(spirvBlobOut)
    {
        gl::ShaderBitSet allStages;
        allStages.set();
        mBuiltinVariableInfo.activeStages = allStages;
    }

    std::vector<const ShaderInterfaceVariableInfo *> &getVariableInfoByIdMap()
    {
        return mVariableInfoById;
    }

    static spirv::IdRef GetNewId(spirv::Blob *blob);
    spirv::IdRef getNewId();

  protected:
    // SPIR-V 1.0 Table 1: First Words of Physical Layout
    enum HeaderIndex
    {
        kHeaderIndexMagic        = 0,
        kHeaderIndexVersion      = 1,
        kHeaderIndexGenerator    = 2,
        kHeaderIndexIndexBound   = 3,
        kHeaderIndexSchema       = 4,
        kHeaderIndexInstructions = 5,
    };

    // Common utilities
    void onTransformBegin();
    const uint32_t *getCurrentInstruction(spv::Op *opCodeOut, uint32_t *wordCountOut) const;
    void copyInstruction(const uint32_t *instruction, size_t wordCount);

    // SPIR-V to transform:
    const spirv::Blob &mSpirvBlobIn;

    // Input shader variable info map:
    const ShaderInterfaceVariableInfoMap &mVariableInfoMap;

    // Transformed SPIR-V:
    spirv::Blob *mSpirvBlobOut;

    // Traversal state:
    size_t mCurrentWord       = 0;
    bool mIsInFunctionSection = false;

    // Transformation state:

    // Shader variable info per id, if id is a shader variable.
    std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById;
    ShaderInterfaceVariableInfo mBuiltinVariableInfo;
};

void SpirvTransformerBase::onTransformBegin()
{
    // Glslang succeeded in outputting SPIR-V, so we assume it's valid.
    ASSERT(mSpirvBlobIn.size() >= kHeaderIndexInstructions);
    // Since SPIR-V comes from a local call to glslang, it necessarily has the same endianness as
    // the running architecture, so no byte-swapping is necessary.
    ASSERT(mSpirvBlobIn[kHeaderIndexMagic] == spv::MagicNumber);

    // Make sure the transformer is not reused to avoid having to reinitialize it here.
    ASSERT(mCurrentWord == 0);
    ASSERT(mIsInFunctionSection == false);

    // Make sure the spirv::Blob is not reused.
    ASSERT(mSpirvBlobOut->empty());

    // Copy the header to SPIR-V blob, we need that to be defined for SpirvTransformerBase::getNewId
    // to work.
    mSpirvBlobOut->assign(mSpirvBlobIn.begin(), mSpirvBlobIn.begin() + kHeaderIndexInstructions);

    mCurrentWord = kHeaderIndexInstructions;
}

const uint32_t *SpirvTransformerBase::getCurrentInstruction(spv::Op *opCodeOut,
                                                            uint32_t *wordCountOut) const
{
    ASSERT(mCurrentWord < mSpirvBlobIn.size());
    const uint32_t *instruction = &mSpirvBlobIn[mCurrentWord];

    spirv::GetInstructionOpAndLength(instruction, opCodeOut, wordCountOut);

    // Since glslang succeeded in producing SPIR-V, we assume it to be valid.
    ASSERT(mCurrentWord + *wordCountOut <= mSpirvBlobIn.size());

    return instruction;
}

void SpirvTransformerBase::copyInstruction(const uint32_t *instruction, size_t wordCount)
{
    mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount);
}

spirv::IdRef SpirvTransformerBase::GetNewId(spirv::Blob *blob)
{
    return spirv::IdRef((*blob)[kHeaderIndexIndexBound]++);
}

spirv::IdRef SpirvTransformerBase::getNewId()
{
    return GetNewId(mSpirvBlobOut);
}

enum class SpirvVariableType
{
    InterfaceVariable,
    BuiltIn,
    Other,
};

enum class TransformationState
{
    Transformed,
    Unchanged,
};

// Helper class that gathers IDs of interest.  This class would be largely unnecessary when the
// translator generates SPIR-V directly, as it could communicate these IDs directly.
class SpirvIDDiscoverer final : angle::NonCopyable
{
  public:
    SpirvIDDiscoverer() : mOutputPerVertex{}, mInputPerVertex{} {}

    void init(size_t indexBound);

    // Instructions:
    void visitDecorate(spirv::IdRef id, spv::Decoration decoration);
    void visitName(spirv::IdRef id, const spirv::LiteralString &name);
    void visitMemberName(const ShaderInterfaceVariableInfo &info,
                         spirv::IdRef id,
                         spirv::LiteralInteger member,
                         const spirv::LiteralString &name);
    void visitTypeArray(spirv::IdResult id, spirv::IdRef elementType, spirv::IdRef length);
    void visitTypeFloat(spirv::IdResult id, spirv::LiteralInteger width);
    void visitTypeInt(spirv::IdResult id,
                      spirv::LiteralInteger width,
                      spirv::LiteralInteger signedness);
    void visitTypePointer(spirv::IdResult id, spv::StorageClass storageClass, spirv::IdRef typeId);
    void visitTypeVector(spirv::IdResult id,
                         spirv::IdRef componentId,
                         spirv::LiteralInteger componentCount);
    SpirvVariableType visitVariable(spirv::IdResultType typeId,
                                    spirv::IdResult id,
                                    spv::StorageClass storageClass,
                                    spirv::LiteralString *nameOut);

    // Helpers:
    void visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId);
    void writePendingDeclarations(spirv::Blob *blobOut);

    // Getters:
    const spirv::LiteralString &getName(spirv::IdRef id) const { return mNamesById[id]; }
    bool isIOBlock(spirv::IdRef id) const { return mIsIOBlockById[id]; }
    bool isPerVertex(spirv::IdRef typeId) const
    {
        return typeId == mOutputPerVertex.typeId || typeId == mInputPerVertex.typeId;
    }
    uint32_t getPerVertexMaxActiveMember(spirv::IdRef typeId) const
    {
        ASSERT(isPerVertex(typeId));
        return typeId == mOutputPerVertex.typeId ? mOutputPerVertex.maxActiveMember
                                                 : mInputPerVertex.maxActiveMember;
    }

    spirv::IdRef floatId() const { return mFloatId; }
    spirv::IdRef vec4Id() const { return mVec4Id; }
    spirv::IdRef vec4OutTypePointerId() const { return mVec4OutTypePointerId; }
    spirv::IdRef intId() const { return mIntId; }
    spirv::IdRef uintId() const { return mUintId; }
    spirv::IdRef int0Id() const { return mInt0Id; }
    spirv::IdRef floatHalfId() const { return mFloatHalfId; }
    spirv::IdRef outputPerVertexTypePointerId() const { return mOutputPerVertexTypePointerId; }
    spirv::IdRef outputPerVertexId() const { return mOutputPerVertexId; }

  private:
    // Names associated with ids through OpName.  The same name may be assigned to multiple ids, but
    // not all names are interesting (for example function arguments).  When the variable
    // declaration is met (OpVariable), the variable info is matched with the corresponding id's
    // name based on the Storage Class.
    std::vector<spirv::LiteralString> mNamesById;

    // Tracks whether a given type is an I/O block.  I/O blocks are identified by their type name
    // instead of variable name, but otherwise look like varyings of struct type (which are
    // identified by their instance name).  To disambiguate them, the `OpDecorate %N Block`
    // instruction is used which decorates I/O block types.
    std::vector<bool> mIsIOBlockById;

    // gl_PerVertex is unique in that it's the only builtin of struct type.  This struct is pruned
    // by removing trailing inactive members.  We therefore need to keep track of what's its type id
    // as well as which is the last active member.  Note that intermediate stages, i.e. geometry and
    // tessellation have two gl_PerVertex declarations, one for input and one for output.
    struct PerVertexData
    {
        spirv::IdRef typeId;
        uint32_t maxActiveMember;
    };
    PerVertexData mOutputPerVertex;
    PerVertexData mInputPerVertex;

    // A handful of ids that are used to generate gl_Position transformation code (for pre-rotation
    // or depth correction).  These IDs are used to load/store gl_Position and apply modifications
    // and swizzles.
    //
    // - mFloatId: id of OpTypeFloat 32
    // - mVec4Id: id of OpTypeVector %mFloatID 4
    // - mVec4OutTypePointerId: id of OpTypePointer Output %mVec4ID
    // - mIntId: id of OpTypeInt 32 1
    // - mUintId: id of OpTypeInt 32 0
    // - mInt0Id: id of OpConstant %mIntID 0
    // - mFloatHalfId: id of OpConstant %mFloatId 0.5f
    // - mOutputPerVertexTypePointerId: id of OpTypePointer Output %mOutputPerVertex.typeId
    // - mOutputPerVertexId: id of OpVariable %mOutputPerVertexTypePointerId Output
    //
    spirv::IdRef mFloatId;
    spirv::IdRef mVec4Id;
    spirv::IdRef mVec4OutTypePointerId;
    spirv::IdRef mIntId;
    spirv::IdRef mUintId;
    spirv::IdRef mInt0Id;
    spirv::IdRef mFloatHalfId;
    spirv::IdRef mOutputPerVertexTypePointerId;
    spirv::IdRef mOutputPerVertexId;
};

void SpirvIDDiscoverer::init(size_t indexBound)
{
    // Allocate storage for id-to-name map.  Used to associate ShaderInterfaceVariableInfo with ids
    // based on name, but only when it's determined that the name corresponds to a shader interface
    // variable.
    mNamesById.resize(indexBound, nullptr);

    // Allocate storage for id-to-flag map.  Used to disambiguate I/O blocks instances from varyings
    // of struct type.
    mIsIOBlockById.resize(indexBound, false);
}

void SpirvIDDiscoverer::visitDecorate(spirv::IdRef id, spv::Decoration decoration)
{
    mIsIOBlockById[id] = decoration == spv::DecorationBlock;
}

void SpirvIDDiscoverer::visitName(spirv::IdRef id, const spirv::LiteralString &name)
{
    // The names and ids are unique
    ASSERT(id < mNamesById.size());
    ASSERT(mNamesById[id] == nullptr);

    mNamesById[id] = name;
}

void SpirvIDDiscoverer::visitMemberName(const ShaderInterfaceVariableInfo &info,
                                        spirv::IdRef id,
                                        spirv::LiteralInteger member,
                                        const spirv::LiteralString &name)
{
    // The names and ids are unique
    ASSERT(id < mNamesById.size());
    ASSERT(mNamesById[id] != nullptr);

    if (strcmp(mNamesById[id], "gl_PerVertex") != 0)
    {
        return;
    }

    // Assume output gl_PerVertex is encountered first.  When the storage class of these types are
    // determined, the variables can be swapped if this assumption was incorrect.
    if (!mOutputPerVertex.typeId.valid() || id == mOutputPerVertex.typeId)
    {
        mOutputPerVertex.typeId = id;

        // Keep track of the range of members that are active.
        if (info.varyingIsOutput && member > mOutputPerVertex.maxActiveMember)
        {
            mOutputPerVertex.maxActiveMember = member;
        }
    }
    else if (!mInputPerVertex.typeId.valid() || id == mInputPerVertex.typeId)
    {
        mInputPerVertex.typeId = id;

        // Keep track of the range of members that are active.
        if (info.varyingIsInput && member > mInputPerVertex.maxActiveMember)
        {
            mInputPerVertex.maxActiveMember = member;
        }
    }
    else
    {
        UNREACHABLE();
    }
}

void SpirvIDDiscoverer::visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId)
{
    // Every type id is declared only once.
    ASSERT(id < mNamesById.size());
    ASSERT(mNamesById[id] == nullptr);
    ASSERT(id < mIsIOBlockById.size());
    ASSERT(!mIsIOBlockById[id]);

    // Carry the name forward from the base type.  This is only necessary for interface blocks,
    // as the variable info is associated with the block name instead of the variable name (to
    // support nameless interface blocks).  When the variable declaration is met, either the
    // type name or the variable name is used to associate with info based on the variable's
    // storage class.
    ASSERT(typeId < mNamesById.size());
    mNamesById[id] = mNamesById[typeId];

    // Similarly, carry forward the information regarding whether this type is an I/O block.
    ASSERT(typeId < mIsIOBlockById.size());
    mIsIOBlockById[id] = mIsIOBlockById[typeId];
}

void SpirvIDDiscoverer::visitTypeArray(spirv::IdResult id,
                                       spirv::IdRef elementType,
                                       spirv::IdRef length)
{
    visitTypeHelper(id, elementType);
}

void SpirvIDDiscoverer::visitTypeFloat(spirv::IdResult id, spirv::LiteralInteger width)
{
    // Only interested in OpTypeFloat 32.
    if (width == 32)
    {
        ASSERT(!mFloatId.valid());
        mFloatId = id;
    }
}

void SpirvIDDiscoverer::visitTypeInt(spirv::IdResult id,
                                     spirv::LiteralInteger width,
                                     spirv::LiteralInteger signedness)
{
    // Only interested in OpTypeInt 32 *.
    if (width != 32)
    {
        return;
    }

    if (signedness == 0)
    {
        ASSERT(!mUintId.valid());
        mUintId = id;
    }
    else
    {
        ASSERT(!mIntId.valid());
        mIntId = id;
    }
}

void SpirvIDDiscoverer::visitTypePointer(spirv::IdResult id,
                                         spv::StorageClass storageClass,
                                         spirv::IdRef typeId)
{
    visitTypeHelper(id, typeId);

    // Verify that the ids associated with input and output gl_PerVertex are correct.
    if (typeId == mOutputPerVertex.typeId || typeId == mInputPerVertex.typeId)
    {
        // If assumption about the first gl_PerVertex encountered being Output is wrong, swap the
        // two ids.
        if ((typeId == mOutputPerVertex.typeId && storageClass == spv::StorageClassInput) ||
            (typeId == mInputPerVertex.typeId && storageClass == spv::StorageClassOutput))
        {
            std::swap(mOutputPerVertex.typeId, mInputPerVertex.typeId);
        }

        // Remember type pointer of output gl_PerVertex for gl_Position transformations.
        if (storageClass == spv::StorageClassOutput)
        {
            mOutputPerVertexTypePointerId = id;
        }
    }

    // If OpTypePointer Output %mVec4ID was encountered, remember that.  Otherwise we'll have to
    // generate one.
    if (typeId == mVec4Id && storageClass == spv::StorageClassOutput)
    {
        mVec4OutTypePointerId = id;
    }
}

void SpirvIDDiscoverer::visitTypeVector(spirv::IdResult id,
                                        spirv::IdRef componentId,
                                        spirv::LiteralInteger componentCount)
{
    // Only interested in OpTypeVector %mFloatId 4
    if (componentId == mFloatId && componentCount == 4)
    {
        ASSERT(!mVec4Id.valid());
        mVec4Id = id;
    }
}

SpirvVariableType SpirvIDDiscoverer::visitVariable(spirv::IdResultType typeId,
                                                   spirv::IdResult id,
                                                   spv::StorageClass storageClass,
                                                   spirv::LiteralString *nameOut)
{
    ASSERT(typeId < mNamesById.size());
    ASSERT(id < mNamesById.size());
    ASSERT(typeId < mIsIOBlockById.size());

    // If storage class indicates that this is not a shader interface variable, ignore it.
    const bool isInterfaceBlockVariable =
        storageClass == spv::StorageClassUniform || storageClass == spv::StorageClassStorageBuffer;
    const bool isOpaqueUniform = storageClass == spv::StorageClassUniformConstant;
    const bool isInOut =
        storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput;

    if (!isInterfaceBlockVariable && !isOpaqueUniform && !isInOut)
    {
        return SpirvVariableType::Other;
    }

    // For interface block variables, the name that's used to associate info is the block name
    // rather than the variable name.
    const bool isIOBlock = mIsIOBlockById[typeId];
    *nameOut             = mNamesById[isInterfaceBlockVariable || isIOBlock ? typeId : id];

    ASSERT(*nameOut != nullptr);

    // Handle builtins, which all start with "gl_".  The variable name could be an indication of a
    // builtin variable (such as with gl_FragCoord).  gl_PerVertex is the only builtin whose "type"
    // name starts with gl_.  However, gl_PerVertex has its own entry in the info map for its
    // potential use with transform feedback.
    const bool isNameBuiltin = isInOut && !isIOBlock && gl::IsBuiltInName(*nameOut);
    if (isNameBuiltin)
    {
        return SpirvVariableType::BuiltIn;
    }

    if (typeId == mOutputPerVertexTypePointerId)
    {
        // If this is the output gl_PerVertex variable, remember its id for gl_Position
        // transformations.
        ASSERT(storageClass == spv::StorageClassOutput && isIOBlock &&
               strcmp(*nameOut, "gl_PerVertex") == 0);
        mOutputPerVertexId = id;
    }

    return SpirvVariableType::InterfaceVariable;
}

void SpirvIDDiscoverer::writePendingDeclarations(spirv::Blob *blobOut)
{
    if (!mFloatId.valid())
    {
        mFloatId = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteTypeFloat(blobOut, mFloatId, spirv::LiteralInteger(32));
    }

    if (!mVec4Id.valid())
    {
        mVec4Id = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteTypeVector(blobOut, mVec4Id, mFloatId, spirv::LiteralInteger(4));
    }

    if (!mVec4OutTypePointerId.valid())
    {
        mVec4OutTypePointerId = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteTypePointer(blobOut, mVec4OutTypePointerId, spv::StorageClassOutput, mVec4Id);
    }

    if (!mIntId.valid())
    {
        mIntId = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteTypeInt(blobOut, mIntId, spirv::LiteralInteger(32), spirv::LiteralInteger(1));
    }

    ASSERT(!mInt0Id.valid());
    mInt0Id = SpirvTransformerBase::GetNewId(blobOut);
    spirv::WriteConstant(blobOut, mIntId, mInt0Id, spirv::LiteralContextDependentNumber(0));

    constexpr uint32_t kFloatHalfAsUint = 0x3F00'0000;

    ASSERT(!mFloatHalfId.valid());
    mFloatHalfId = SpirvTransformerBase::GetNewId(blobOut);
    spirv::WriteConstant(blobOut, mFloatId, mFloatHalfId,
                         spirv::LiteralContextDependentNumber(kFloatHalfAsUint));
}

// Helper class that trims input and output gl_PerVertex declarations to remove inactive builtins.
class SpirvPerVertexTrimmer final : angle::NonCopyable
{
  public:
    SpirvPerVertexTrimmer() {}

    TransformationState transformMemberDecorate(const SpirvIDDiscoverer &ids,
                                                spirv::IdRef typeId,
                                                spirv::LiteralInteger member,
                                                spv::Decoration decoration);
    TransformationState transformMemberName(const SpirvIDDiscoverer &ids,
                                            spirv::IdRef id,
                                            spirv::LiteralInteger member,
                                            const spirv::LiteralString &name);
    TransformationState transformTypeStruct(const SpirvIDDiscoverer &ids,
                                            spirv::IdResult id,
                                            spirv::IdRefList *memberList,
                                            spirv::Blob *blobOut);
};

TransformationState SpirvPerVertexTrimmer::transformMemberDecorate(const SpirvIDDiscoverer &ids,
                                                                   spirv::IdRef typeId,
                                                                   spirv::LiteralInteger member,
                                                                   spv::Decoration decoration)
{
    // Transform only OpMemberDecorate %gl_PerVertex N BuiltIn B
    if (!ids.isPerVertex(typeId) || decoration != spv::DecorationBuiltIn)
    {
        return TransformationState::Unchanged;
    }

    // Drop stripped fields.
    return member > ids.getPerVertexMaxActiveMember(typeId) ? TransformationState::Transformed
                                                            : TransformationState::Unchanged;
}

TransformationState SpirvPerVertexTrimmer::transformMemberName(const SpirvIDDiscoverer &ids,
                                                               spirv::IdRef id,
                                                               spirv::LiteralInteger member,
                                                               const spirv::LiteralString &name)
{
    // Remove the instruction if it's a stripped member of gl_PerVertex.
    return ids.isPerVertex(id) && member > ids.getPerVertexMaxActiveMember(id)
               ? TransformationState::Transformed
               : TransformationState::Unchanged;
}

TransformationState SpirvPerVertexTrimmer::transformTypeStruct(const SpirvIDDiscoverer &ids,
                                                               spirv::IdResult id,
                                                               spirv::IdRefList *memberList,
                                                               spirv::Blob *blobOut)
{
    if (!ids.isPerVertex(id))
    {
        return TransformationState::Unchanged;
    }

    const uint32_t maxMembers = ids.getPerVertexMaxActiveMember(id);

    // Change the definition of the gl_PerVertex struct by stripping unused fields at the end.
    const uint32_t memberCount = maxMembers + 1;
    memberList->resize(memberCount);

    spirv::WriteTypeStruct(blobOut, id, *memberList);

    return TransformationState::Transformed;
}

// Helper class that removes inactive varyings and replaces them with Private variables.
class SpirvInactiveVaryingRemover final : angle::NonCopyable
{
  public:
    SpirvInactiveVaryingRemover() {}

    void init(size_t indexCount);

    TransformationState transformAccessChain(spirv::IdResultType typeId,
                                             spirv::IdResult id,
                                             spirv::IdRef baseId,
                                             const spirv::IdRefList &indexList,
                                             spirv::Blob *blobOut);
    TransformationState transformDecorate(const ShaderInterfaceVariableInfo &info,
                                          gl::ShaderType shaderType,
                                          spirv::IdRef id,
                                          spv::Decoration decoration,
                                          const spirv::LiteralIntegerList &decorationValues,
                                          spirv::Blob *blobOut);
    TransformationState transformTypePointer(const SpirvIDDiscoverer &ids,
                                             spirv::IdResult id,
                                             spv::StorageClass storageClass,
                                             spirv::IdRef typeId,
                                             spirv::Blob *blobOut);
    TransformationState transformVariable(spirv::IdResultType typeId,
                                          spirv::IdResult id,
                                          spv::StorageClass storageClass,
                                          spirv::Blob *blobOut);

    void modifyEntryPointInterfaceList(
        const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
        gl::ShaderType shaderType,
        spirv::IdRefList *interfaceList);

  private:
    // Each OpTypePointer instruction that defines a type with the Output storage class is
    // duplicated with a similar instruction but which defines a type with the Private storage
    // class.  If inactive varyings are encountered, its type is changed to the Private one.  The
    // following vector maps the Output type id to the corresponding Private one.
    std::vector<spirv::IdRef> mTypePointerTransformedId;
};

void SpirvInactiveVaryingRemover::init(size_t indexBound)
{
    // Allocate storage for Output type pointer map.  At index i, this vector holds the identical
    // type as %i except for its storage class turned to Private.
    mTypePointerTransformedId.resize(indexBound);
}

TransformationState SpirvInactiveVaryingRemover::transformAccessChain(
    spirv::IdResultType typeId,
    spirv::IdResult id,
    spirv::IdRef baseId,
    const spirv::IdRefList &indexList,
    spirv::Blob *blobOut)
{
    // Modifiy the instruction to use the private type.
    ASSERT(typeId < mTypePointerTransformedId.size());
    ASSERT(mTypePointerTransformedId[typeId].valid());

    spirv::WriteAccessChain(blobOut, mTypePointerTransformedId[typeId], id, baseId, indexList);

    return TransformationState::Transformed;
}

TransformationState SpirvInactiveVaryingRemover::transformDecorate(
    const ShaderInterfaceVariableInfo &info,
    gl::ShaderType shaderType,
    spirv::IdRef id,
    spv::Decoration decoration,
    const spirv::LiteralIntegerList &decorationValues,
    spirv::Blob *blobOut)
{
    // If it's an inactive varying, remove the decoration altogether.
    return info.activeStages[shaderType] ? TransformationState::Unchanged
                                         : TransformationState::Transformed;
}

void SpirvInactiveVaryingRemover::modifyEntryPointInterfaceList(
    const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
    gl::ShaderType shaderType,
    spirv::IdRefList *interfaceList)
{
    // Filter out inactive varyings from entry point interface declaration.
    size_t writeIndex = 0;
    for (size_t index = 0; index < interfaceList->size(); ++index)
    {
        spirv::IdRef id((*interfaceList)[index]);
        const ShaderInterfaceVariableInfo *info = variableInfoById[id];

        ASSERT(info);

        if (!info->activeStages[shaderType])
        {
            continue;
        }

        (*interfaceList)[writeIndex] = id;
        ++writeIndex;
    }

    // Update the number of interface variables.
    interfaceList->resize(writeIndex);
}

TransformationState SpirvInactiveVaryingRemover::transformTypePointer(
    const SpirvIDDiscoverer &ids,
    spirv::IdResult id,
    spv::StorageClass storageClass,
    spirv::IdRef typeId,
    spirv::Blob *blobOut)
{
    // If the storage class is output, this may be used to create a variable corresponding to an
    // inactive varying, or if that varying is a struct, an Op*AccessChain retrieving a field of
    // that inactive varying.
    //
    // SPIR-V specifies the storage class both on the type and the variable declaration.  Otherwise
    // it would have been sufficient to modify the OpVariable instruction. For simplicity, duplicate
    // every "OpTypePointer Output" and "OpTypePointer Input" instruction except with the Private
    // storage class, in case it may be necessary later.

    // Cannot create a Private type declaration from builtins such as gl_PerVertex.
    if (ids.getName(typeId) != nullptr && gl::IsBuiltInName(ids.getName(typeId)))
    {
        return TransformationState::Unchanged;
    }

    if (storageClass != spv::StorageClassOutput && storageClass != spv::StorageClassInput)
    {
        return TransformationState::Unchanged;
    }

    const spirv::IdRef newPrivateTypeId(SpirvTransformerBase::GetNewId(blobOut));

    // Write OpTypePointer for the new PrivateType.
    spirv::WriteTypePointer(blobOut, newPrivateTypeId, spv::StorageClassPrivate, typeId);

    // Remember the id of the replacement.
    ASSERT(id < mTypePointerTransformedId.size());
    mTypePointerTransformedId[id] = newPrivateTypeId;

    // The original instruction should still be present as well.  At this point, we don't know
    // whether we will need the original or Private type.
    return TransformationState::Unchanged;
}

TransformationState SpirvInactiveVaryingRemover::transformVariable(spirv::IdResultType typeId,
                                                                   spirv::IdResult id,
                                                                   spv::StorageClass storageClass,
                                                                   spirv::Blob *blobOut)
{
    ASSERT(storageClass == spv::StorageClassOutput || storageClass == spv::StorageClassInput);

    ASSERT(typeId < mTypePointerTransformedId.size());
    ASSERT(mTypePointerTransformedId[typeId].valid());
    spirv::WriteVariable(blobOut, mTypePointerTransformedId[typeId], id, spv::StorageClassPrivate,
                         nullptr);

    return TransformationState::Transformed;
}

// Helper class that fixes varying precisions so they match between shader stages.
class SpirvVaryingPrecisionFixer final : angle::NonCopyable
{
  public:
    SpirvVaryingPrecisionFixer() {}

    void init(size_t indexBound);

    void visitTypePointer(spirv::IdResult id, spv::StorageClass storageClass, spirv::IdRef typeId);
    void visitVariable(const ShaderInterfaceVariableInfo &info,
                       gl::ShaderType shaderType,
                       spirv::IdResultType typeId,
                       spirv::IdResult id,
                       spv::StorageClass storageClass,
                       spirv::Blob *blobOut);

    TransformationState transformVariable(const ShaderInterfaceVariableInfo &info,
                                          spirv::IdResultType typeId,
                                          spirv::IdResult id,
                                          spv::StorageClass storageClass,
                                          spirv::Blob *blobOut);

    void modifyEntryPointInterfaceList(spirv::IdRefList *interfaceList);
    void addDecorate(spirv::IdRef replacedId, spirv::Blob *blobOut);
    void writeInputPreamble(
        const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
        gl::ShaderType shaderType,
        spirv::Blob *blobOut);
    void writeOutputPrologue(
        const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
        gl::ShaderType shaderType,
        spirv::Blob *blobOut);

    bool isReplaced(spirv::IdRef id) const { return mFixedVaryingId[id].valid(); }
    spirv::IdRef getReplacementId(spirv::IdRef id) const
    {
        return mFixedVaryingId[id].valid() ? mFixedVaryingId[id] : id;
    }

  private:
    std::vector<spirv::IdRef> mTypePointerTypeId;
    std::vector<spirv::IdRef> mFixedVaryingId;
    std::vector<spirv::IdRef> mFixedVaryingTypeId;
};

void SpirvVaryingPrecisionFixer::init(size_t indexBound)
{
    // Allocate storage for precision mismatch fix up.
    mTypePointerTypeId.resize(indexBound);
    mFixedVaryingId.resize(indexBound);
    mFixedVaryingTypeId.resize(indexBound);
}

void SpirvVaryingPrecisionFixer::visitTypePointer(spirv::IdResult id,
                                                  spv::StorageClass storageClass,
                                                  spirv::IdRef typeId)
{
    mTypePointerTypeId[id] = typeId;
}

void SpirvVaryingPrecisionFixer::visitVariable(const ShaderInterfaceVariableInfo &info,
                                               gl::ShaderType shaderType,
                                               spirv::IdResultType typeId,
                                               spirv::IdResult id,
                                               spv::StorageClass storageClass,
                                               spirv::Blob *blobOut)
{
    if (info.useRelaxedPrecision && info.activeStages[shaderType] && !mFixedVaryingId[id].valid())
    {
        mFixedVaryingId[id]     = SpirvTransformerBase::GetNewId(blobOut);
        mFixedVaryingTypeId[id] = typeId;
    }
}

TransformationState SpirvVaryingPrecisionFixer::transformVariable(
    const ShaderInterfaceVariableInfo &info,
    spirv::IdResultType typeId,
    spirv::IdResult id,
    spv::StorageClass storageClass,
    spirv::Blob *blobOut)
{
    if (info.useRelaxedPrecision &&
        (storageClass == spv::StorageClassOutput || storageClass == spv::StorageClassInput))
    {
        // Change existing OpVariable to use fixedVaryingId
        ASSERT(mFixedVaryingId[id].valid());
        spirv::WriteVariable(blobOut, typeId, mFixedVaryingId[id], storageClass, nullptr);

        return TransformationState::Transformed;
    }
    return TransformationState::Unchanged;
}

void SpirvVaryingPrecisionFixer::writeInputPreamble(
    const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
    gl::ShaderType shaderType,
    spirv::Blob *blobOut)
{
    if (shaderType == gl::ShaderType::Vertex || shaderType == gl::ShaderType::Compute)
    {
        return;
    }

    // Copy from corrected varyings to temp global variables with original precision.
    for (uint32_t idIndex = spirv::kMinValidId; idIndex < variableInfoById.size(); idIndex++)
    {
        const spirv::IdRef id(idIndex);
        const ShaderInterfaceVariableInfo *info = variableInfoById[id];
        if (info && info->useRelaxedPrecision && info->activeStages[shaderType] &&
            info->varyingIsInput)
        {
            // This is an input varying, need to cast the mediump value that came from
            // the previous stage into a highp value that the code wants to work with.
            ASSERT(mFixedVaryingTypeId[id].valid());

            // Build OpLoad instruction to load the mediump value into a temporary
            const spirv::IdRef tempVar(SpirvTransformerBase::GetNewId(blobOut));
            const spirv::IdRef tempVarType(mTypePointerTypeId[mFixedVaryingTypeId[id]]);
            ASSERT(tempVarType.valid());

            spirv::WriteLoad(blobOut, tempVarType, tempVar, mFixedVaryingId[id], nullptr);

            // Build OpStore instruction to cast the mediump value to highp for use in
            // the function
            spirv::WriteStore(blobOut, id, tempVar, nullptr);
        }
    }
}

void SpirvVaryingPrecisionFixer::modifyEntryPointInterfaceList(spirv::IdRefList *interfaceList)
{
    // Modify interface list if any ID was replaced due to varying precision mismatch.
    for (size_t index = 0; index < interfaceList->size(); ++index)
    {
        (*interfaceList)[index] = getReplacementId((*interfaceList)[index]);
    }
}

void SpirvVaryingPrecisionFixer::addDecorate(spirv::IdRef replacedId, spirv::Blob *blobOut)
{
    spirv::WriteDecorate(blobOut, replacedId, spv::DecorationRelaxedPrecision, {});
}

void SpirvVaryingPrecisionFixer::writeOutputPrologue(
    const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
    gl::ShaderType shaderType,
    spirv::Blob *blobOut)
{
    if (shaderType == gl::ShaderType::Fragment || shaderType == gl::ShaderType::Compute)
    {
        return;
    }

    // Copy from temp global variables with original precision to corrected varyings.
    for (uint32_t idIndex = spirv::kMinValidId; idIndex < variableInfoById.size(); idIndex++)
    {
        const spirv::IdRef id(idIndex);
        const ShaderInterfaceVariableInfo *info = variableInfoById[id];
        if (info && info->useRelaxedPrecision && info->activeStages[shaderType] &&
            info->varyingIsOutput)
        {
            ASSERT(mFixedVaryingTypeId[id].valid());

            // Build OpLoad instruction to load the highp value into a temporary
            const spirv::IdRef tempVar(SpirvTransformerBase::GetNewId(blobOut));
            const spirv::IdRef tempVarType(mTypePointerTypeId[mFixedVaryingTypeId[id]]);
            ASSERT(tempVarType.valid());

            spirv::WriteLoad(blobOut, tempVarType, tempVar, id, nullptr);

            // Build OpStore instruction to cast the highp value to mediump for output
            spirv::WriteStore(blobOut, mFixedVaryingId[id], tempVar, nullptr);
        }
    }
}

// Helper class that generates code for transform feedback
class SpirvTransformFeedbackCodeGenerator final : angle::NonCopyable
{
  public:
    SpirvTransformFeedbackCodeGenerator(bool isEmulated)
        : mIsEmulated(isEmulated), mHasTransformFeedbackOutput(false)
    {}

    void visitName(spirv::IdRef id, const spirv::LiteralString &name);
    void visitTypeVector(const SpirvIDDiscoverer &ids,
                         spirv::IdResult id,
                         spirv::IdRef componentId,
                         spirv::LiteralInteger componentCount);
    void visitTypePointer(spirv::IdResult id, spv::StorageClass storageClass, spirv::IdRef typeId);
    void visitVariable(const ShaderInterfaceVariableInfo &info,
                       gl::ShaderType shaderType,
                       const spirv::LiteralString &name,
                       spirv::IdResultType typeId,
                       spirv::IdResult id,
                       spv::StorageClass storageClass);

    TransformationState transformCapability(spv::Capability capability, spirv::Blob *blobOut);
    TransformationState transformName(spirv::IdRef id, spirv::LiteralString name);
    TransformationState transformVariable(const ShaderInterfaceVariableInfo &info,
                                          const ShaderInterfaceVariableInfoMap &variableInfoMap,
                                          gl::ShaderType shaderType,
                                          spirv::IdResultType typeId,
                                          spirv::IdResult id,
                                          spv::StorageClass storageClass);

    void writePendingDeclarations(
        const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
        const SpirvIDDiscoverer &ids,
        spirv::Blob *blobOut);
    void writeTransformFeedbackExtensionOutput(const SpirvIDDiscoverer &ids,
                                               spirv::IdRef positionId,
                                               spirv::Blob *blobOut);
    void writeTransformFeedbackEmulationOutput(
        const SpirvIDDiscoverer &ids,
        const SpirvVaryingPrecisionFixer &varyingPrecisionFixer,
        spirv::IdRef currentFunctionId,
        spirv::Blob *blobOut);
    void addExecutionMode(spirv::IdRef entryPointId, spirv::Blob *blobOut);
    void addMemberDecorate(const ShaderInterfaceVariableInfo &info,
                           spirv::IdRef id,
                           spirv::Blob *blobOut);
    void addDecorate(const ShaderInterfaceVariableInfo &info,
                     spirv::IdRef id,
                     spirv::Blob *blobOut);

  private:
    void gatherXfbVaryings(const ShaderInterfaceVariableInfo &info, spirv::IdRef id);
    void visitXfbVarying(const ShaderInterfaceVariableXfbInfo &xfb,
                         spirv::IdRef baseId,
                         uint32_t fieldIndex);
    void writeIntConstant(const SpirvIDDiscoverer &ids,
                          uint32_t value,
                          spirv::IdRef intId,
                          spirv::Blob *blobOut);
    void getVaryingTypeIds(const SpirvIDDiscoverer &ids,
                           GLenum componentType,
                           bool isPrivate,
                           spirv::IdRef *typeIdOut,
                           spirv::IdRef *typePtrOut);
    void writeGetOffsetsCall(spirv::IdRef xfbOffsets, spirv::Blob *blobOut);
    void writeComponentCapture(const SpirvIDDiscoverer &ids,
                               uint32_t bufferIndex,
                               spirv::IdRef xfbOffset,
                               spirv::IdRef varyingTypeId,
                               spirv::IdRef varyingTypePtr,
                               spirv::IdRef varyingBaseId,
                               const spirv::IdRefList &accessChainIndices,
                               GLenum componentType,
                               spirv::Blob *blobOut);

    static constexpr size_t kXfbDecorationCount                           = 3;
    static constexpr spv::Decoration kXfbDecorations[kXfbDecorationCount] = {
        spv::DecorationXfbBuffer,
        spv::DecorationXfbStride,
        spv::DecorationOffset,
    };

    bool mIsEmulated;
    bool mHasTransformFeedbackOutput;

    // Ids needed to generate transform feedback support code.
    spirv::IdRef mTransformFeedbackExtensionPositionId;
    spirv::IdRef mGetXfbOffsetsFuncId;
    spirv::IdRef mXfbCaptureFuncId;
    gl::TransformFeedbackBuffersArray<spirv::IdRef> mXfbBuffers;
    gl::TransformFeedbackBuffersArray<spirv::IdRef> mBufferStrides;
    spirv::IdRef mBufferStridesCompositeId;

    // Type and constant ids:
    //
    // - mIVec4Id: id of OpTypeVector %mIntId 4
    //
    // - mFloatOutputPointerId: id of OpTypePointer Output %mFloatId
    // - mIntOutputPointerId: id of OpTypePointer Output %mIntId
    // - mUintOutputPointerId: id of OpTypePointer Output %mUintId
    // - mFloatPrivatePointerId, mIntPrivatePointerId, mUintPrivatePointerId: identical to the
    //   above, but with the Private storage class.  Used to load from varyings that have been
    //   replaced as part of precision mismatch fixup.
    // - mFloatUniformPointerId: id of OpTypePointer Uniform %mFloatId
    // - mIVec4FuncPointerId: id of OpTypePointer Function %mIVec4Id
    //
    // - mIntNIds[n]: id of OpConstant %mIntId n
    spirv::IdRef mIVec4Id;
    spirv::IdRef mFloatOutputPointerId;
    spirv::IdRef mIntOutputPointerId;
    spirv::IdRef mUintOutputPointerId;
    spirv::IdRef mFloatPrivatePointerId;
    spirv::IdRef mIntPrivatePointerId;
    spirv::IdRef mUintPrivatePointerId;
    spirv::IdRef mFloatUniformPointerId;
    spirv::IdRef mIVec4FuncPointerId;
    // Id of constants such as row, column and array index.  Integers 0, 1, 2 and 3 are always
    // defined due to the ubiquity of usage.
    angle::FastVector<spirv::IdRef, 4> mIntNIds;

    // For transform feedback emulation, the captured elements are gathered in a list and sorted.
    // This allows the output generation code to always use offset += 1, thus relying on only one
    // constant (1).
    struct XfbVarying
    {
        // The varyings are sorted by info.offset.
        const ShaderInterfaceVariableXfbInfo *info;
        // Id of the base variable.
        spirv::IdRef baseId;
        // The field index, if a member of an I/O blocks
        uint32_t fieldIndex;
    };
    gl::TransformFeedbackBuffersArray<std::vector<XfbVarying>> mXfbVaryings;
};

constexpr size_t SpirvTransformFeedbackCodeGenerator::kXfbDecorationCount;
constexpr spv::Decoration SpirvTransformFeedbackCodeGenerator::kXfbDecorations[kXfbDecorationCount];

void SpirvTransformFeedbackCodeGenerator::visitName(spirv::IdRef id,
                                                    const spirv::LiteralString &name)
{
    if (!mIsEmulated)
    {
        return;
    }

    const size_t bufferNameBaseLength = strlen(sh::vk::kXfbEmulationBufferName);

    if (angle::BeginsWith(name, sh::vk::kXfbEmulationGetOffsetsFunctionName))
    {
        ASSERT(!mGetXfbOffsetsFuncId.valid());
        mGetXfbOffsetsFuncId = id;
    }
    else if (angle::BeginsWith(name, sh::vk::kXfbEmulationCaptureFunctionName))
    {
        ASSERT(!mXfbCaptureFuncId.valid());
        mXfbCaptureFuncId = id;
    }
    else if (angle::BeginsWith(name, sh::vk::kXfbEmulationBufferName) &&
             std::isdigit(name[bufferNameBaseLength]))
    {
        static_assert(gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS < 10,
                      "Parsing the xfb buffer index below must be adjusted");
        uint32_t xfbBuffer     = name[bufferNameBaseLength] - '0';
        mXfbBuffers[xfbBuffer] = id;
    }
}

void SpirvTransformFeedbackCodeGenerator::visitTypeVector(const SpirvIDDiscoverer &ids,
                                                          spirv::IdResult id,
                                                          spirv::IdRef componentId,
                                                          spirv::LiteralInteger componentCount)
{
    // Only interested in OpTypeVector %mIntId 4
    if (componentId == ids.intId() && componentCount == 4)
    {
        ASSERT(!mIVec4Id.valid());
        mIVec4Id = id;
    }
}

void SpirvTransformFeedbackCodeGenerator::visitTypePointer(spirv::IdResult id,
                                                           spv::StorageClass storageClass,
                                                           spirv::IdRef typeId)
{
    if (typeId == mIVec4Id && storageClass == spv::StorageClassFunction)
    {
        ASSERT(!mIVec4FuncPointerId.valid());
        mIVec4FuncPointerId = id;
    }
}

void SpirvTransformFeedbackCodeGenerator::visitVariable(const ShaderInterfaceVariableInfo &info,
                                                        gl::ShaderType shaderType,
                                                        const spirv::LiteralString &name,
                                                        spirv::IdResultType typeId,
                                                        spirv::IdResult id,
                                                        spv::StorageClass storageClass)
{
    if (mIsEmulated)
    {
        gatherXfbVaryings(info, id);
        return;
    }

    // Note if the variable is captured by transform feedback.  In that case, the TransformFeedback
    // capability needs to be added.
    if ((info.xfb.buffer != ShaderInterfaceVariableInfo::kInvalid || !info.fieldXfb.empty()) &&
        info.activeStages[shaderType])
    {
        mHasTransformFeedbackOutput = true;

        // If this is the special ANGLEXfbPosition variable, remember its id to be used for the
        // ANGLEXfbPosition = gl_Position; assignment code generation.
        if (strcmp(name, sh::vk::kXfbExtensionPositionOutName) == 0)
        {
            mTransformFeedbackExtensionPositionId = id;
        }
    }
}

TransformationState SpirvTransformFeedbackCodeGenerator::transformCapability(
    spv::Capability capability,
    spirv::Blob *blobOut)
{
    if (!mHasTransformFeedbackOutput || mIsEmulated)
    {
        return TransformationState::Unchanged;
    }

    // Transform feedback capability shouldn't have already been specified.
    ASSERT(capability != spv::CapabilityTransformFeedback);

    // Vulkan shaders have either Shader, Geometry or Tessellation capability.  We find this
    // capability, and add the TransformFeedback capability right before it.
    if (capability != spv::CapabilityShader && capability != spv::CapabilityGeometry &&
        capability != spv::CapabilityTessellation)
    {
        return TransformationState::Unchanged;
    }

    // Write the TransformFeedback capability declaration.
    spirv::WriteCapability(blobOut, spv::CapabilityTransformFeedback);

    // The original capability is retained.
    return TransformationState::Unchanged;
}

TransformationState SpirvTransformFeedbackCodeGenerator::transformName(spirv::IdRef id,
                                                                       spirv::LiteralString name)
{
    // In the case of ANGLEXfbN, unconditionally remove the variable names.  If transform
    // feedback is not active, the corresponding variables will be removed.
    return angle::BeginsWith(name, sh::vk::kXfbEmulationBufferName)
               ? TransformationState::Transformed
               : TransformationState::Unchanged;
}

TransformationState SpirvTransformFeedbackCodeGenerator::transformVariable(
    const ShaderInterfaceVariableInfo &info,
    const ShaderInterfaceVariableInfoMap &variableInfoMap,
    gl::ShaderType shaderType,
    spirv::IdResultType typeId,
    spirv::IdResult id,
    spv::StorageClass storageClass)
{
    // This function is currently called for inactive variables.
    ASSERT(!info.activeStages[shaderType]);

    if (shaderType == gl::ShaderType::Vertex && storageClass == spv::StorageClassUniform)
    {
        // The ANGLEXfbN variables are unconditionally generated and may be inactive.  Remove these
        // variables in that case.
        ASSERT(&info == &variableInfoMap.get(shaderType, GetXfbBufferName(0)) ||
               &info == &variableInfoMap.get(shaderType, GetXfbBufferName(1)) ||
               &info == &variableInfoMap.get(shaderType, GetXfbBufferName(2)) ||
               &info == &variableInfoMap.get(shaderType, GetXfbBufferName(3)));

        // Drop the declaration.
        return TransformationState::Transformed;
    }

    return TransformationState::Unchanged;
}

void SpirvTransformFeedbackCodeGenerator::gatherXfbVaryings(const ShaderInterfaceVariableInfo &info,
                                                            spirv::IdRef id)
{
    visitXfbVarying(info.xfb, id, ShaderInterfaceVariableXfbInfo::kInvalid);

    for (size_t fieldIndex = 0; fieldIndex < info.fieldXfb.size(); ++fieldIndex)
    {
        visitXfbVarying(info.fieldXfb[fieldIndex], id, static_cast<uint32_t>(fieldIndex));
    }
}

void SpirvTransformFeedbackCodeGenerator::visitXfbVarying(const ShaderInterfaceVariableXfbInfo &xfb,
                                                          spirv::IdRef baseId,
                                                          uint32_t fieldIndex)
{
    for (const ShaderInterfaceVariableXfbInfo &arrayElement : xfb.arrayElements)
    {
        visitXfbVarying(arrayElement, baseId, fieldIndex);
    }

    if (xfb.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
    {
        return;
    }

    // Varyings captured to the same buffer have the same stride.
    ASSERT(mXfbVaryings[xfb.buffer].empty() ||
           mXfbVaryings[xfb.buffer][0].info->stride == xfb.stride);

    mXfbVaryings[xfb.buffer].push_back({&xfb, baseId, fieldIndex});
}

void SpirvTransformFeedbackCodeGenerator::writeIntConstant(const SpirvIDDiscoverer &ids,
                                                           uint32_t value,
                                                           spirv::IdRef intId,
                                                           spirv::Blob *blobOut)
{
    if (value == ShaderInterfaceVariableXfbInfo::kInvalid)
    {
        return;
    }

    if (mIntNIds.size() <= value)
    {
        mIntNIds.resize(value + 1);
    }
    else if (mIntNIds[value].valid())
    {
        return;
    }

    mIntNIds[value] = SpirvTransformerBase::GetNewId(blobOut);
    spirv::WriteConstant(blobOut, ids.intId(), mIntNIds[value],
                         spirv::LiteralContextDependentNumber(value));
}

void SpirvTransformFeedbackCodeGenerator::writePendingDeclarations(
    const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
    const SpirvIDDiscoverer &ids,
    spirv::Blob *blobOut)
{
    if (!mIsEmulated)
    {
        return;
    }

    ASSERT(mIVec4Id.valid());

    mFloatOutputPointerId  = SpirvTransformerBase::GetNewId(blobOut);
    mFloatPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
    spirv::WriteTypePointer(blobOut, mFloatOutputPointerId, spv::StorageClassOutput, ids.floatId());
    spirv::WriteTypePointer(blobOut, mFloatPrivatePointerId, spv::StorageClassPrivate,
                            ids.floatId());

    if (ids.intId().valid())
    {
        mIntOutputPointerId  = SpirvTransformerBase::GetNewId(blobOut);
        mIntPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteTypePointer(blobOut, mIntOutputPointerId, spv::StorageClassOutput, ids.intId());
        spirv::WriteTypePointer(blobOut, mIntPrivatePointerId, spv::StorageClassPrivate,
                                ids.intId());
    }

    if (ids.uintId().valid())
    {
        mUintOutputPointerId  = SpirvTransformerBase::GetNewId(blobOut);
        mUintPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteTypePointer(blobOut, mUintOutputPointerId, spv::StorageClassOutput,
                                ids.uintId());
        spirv::WriteTypePointer(blobOut, mUintPrivatePointerId, spv::StorageClassPrivate,
                                ids.uintId());
    }

    mFloatUniformPointerId = SpirvTransformerBase::GetNewId(blobOut);
    spirv::WriteTypePointer(blobOut, mFloatUniformPointerId, spv::StorageClassUniform,
                            ids.floatId());

    mIntNIds.resize(4);
    mIntNIds[0] = ids.int0Id();
    for (int n = 1; n < 4; ++n)
    {
        writeIntConstant(ids, n, ids.intId(), blobOut);
    }

    spirv::IdRefList compositeIds;
    for (const std::vector<XfbVarying> &varyings : mXfbVaryings)
    {
        if (varyings.empty())
        {
            compositeIds.push_back(ids.int0Id());
            continue;
        }

        const ShaderInterfaceVariableXfbInfo *info0 = varyings[0].info;

        // Define the buffer stride constant
        ASSERT(info0->stride % sizeof(float) == 0);
        uint32_t stride = info0->stride / sizeof(float);

        mBufferStrides[info0->buffer] = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteConstant(blobOut, ids.intId(), mBufferStrides[info0->buffer],
                             spirv::LiteralContextDependentNumber(stride));

        compositeIds.push_back(mBufferStrides[info0->buffer]);

        // Define all the constants that would be necessary to load the components of the varying.
        for (const XfbVarying &varying : varyings)
        {
            writeIntConstant(ids, varying.fieldIndex, ids.intId(), blobOut);
            const ShaderInterfaceVariableXfbInfo *info = varying.info;
            if (info->arraySize == ShaderInterfaceVariableXfbInfo::kInvalid)
            {
                continue;
            }

            uint32_t arrayIndexStart =
                varying.info->arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid
                    ? varying.info->arrayIndex
                    : 0;
            uint32_t arrayIndexEnd = arrayIndexStart + info->arraySize;

            for (uint32_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
            {
                writeIntConstant(ids, arrayIndex, ids.intId(), blobOut);
            }
        }
    }

    mBufferStridesCompositeId = SpirvTransformerBase::GetNewId(blobOut);
    spirv::WriteConstantComposite(blobOut, mIVec4Id, mBufferStridesCompositeId, compositeIds);
}

void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackExtensionOutput(
    const SpirvIDDiscoverer &ids,
    spirv::IdRef positionId,
    spirv::Blob *blobOut)
{
    if (mIsEmulated)
    {
        return;
    }

    if (mTransformFeedbackExtensionPositionId.valid())
    {
        spirv::WriteStore(blobOut, mTransformFeedbackExtensionPositionId, positionId, nullptr);
    }
}

class AccessChainIndexListAppend final : angle::NonCopyable
{
  public:
    AccessChainIndexListAppend(bool condition,
                               angle::FastVector<spirv::IdRef, 4> intNIds,
                               uint32_t index,
                               spirv::IdRefList *indexList)
        : mCondition(condition), mIndexList(indexList)
    {
        if (mCondition)
        {
            mIndexList->push_back(intNIds[index]);
        }
    }
    ~AccessChainIndexListAppend()
    {
        if (mCondition)
        {
            mIndexList->pop_back();
        }
    }

  private:
    bool mCondition;
    spirv::IdRefList *mIndexList;
};

void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackEmulationOutput(
    const SpirvIDDiscoverer &ids,
    const SpirvVaryingPrecisionFixer &varyingPrecisionFixer,
    spirv::IdRef currentFunctionId,
    spirv::Blob *blobOut)
{
    if (!mIsEmulated || !mXfbCaptureFuncId.valid() || currentFunctionId != mXfbCaptureFuncId)
    {
        return;
    }

    // First, sort the varyings by offset, to simplify calculation of the output offset.
    for (std::vector<XfbVarying> &varyings : mXfbVaryings)
    {
        std::sort(varyings.begin(), varyings.end(),
                  [](const XfbVarying &first, const XfbVarying &second) {
                      return first.info->offset < second.info->offset;
                  });
    }

    // The following code is generated for transform feedback emulation:
    //
    //     ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
    //     // For buffer N:
    //     int xfbOffset = xfbOffsets[N]
    //     ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
    //     xfbOffset += 1;
    //     ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col + 1]
    //     xfbOffset += 1;
    //     ...
    //
    // The following pieces of SPIR-V code are generated according to the above:
    //
    // - For the initial offsets calculation:
    //
    //     %getOffsetsParam = OpVariable %mIVec4FuncPointerId Function %stridesComposite
    //    %xfbOffsetsResult = OpFunctionCall %ivec4 %ANGLEGetXfbOffsets %stridesComposite
    //       %xfbOffsetsVar = OpVariable %mIVec4FuncPointerId Function
    //                        OpStore %xfbOffsetsVar %xfbOffsetsResult
    //          %xfbOffsets = OpLoad %ivec4 %xfbOffsetsVar
    //
    // - Initial code for each buffer N:
    //
    //           %xfbOffset = OpCompositeExtract %int %xfbOffsets N
    //
    // - For each varying being captured:
    //
    //                        // Load the component
    //        %componentPtr = OpAccessChain %floatOutputPtr %baseId %field %arrayIndex %row %col
    //           %component = OpLoad %float %componentPtr
    //                        // Store in xfb output
    //           %xfbOutPtr = OpAccessChain %floatUniformPtr %xfbBufferN %int0 %xfbOffset
    //                        OpStore %xfbOutPtr %component
    //                        // Increment offset
    //           %xfbOffset = OpIAdd %int %xfbOffset %int1
    //
    //   Note that if the varying being captured is integer, the first two instructions above would
    //   use the intger equivalent types, and the following instruction would bitcast it to float
    //   for storage:
    //
    //             %asFloat = OpBitcast %float %component
    //

    const spirv::IdRef xfbOffsets(SpirvTransformerBase::GetNewId(blobOut));

    // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
    writeGetOffsetsCall(xfbOffsets, blobOut);

    // Go over the buffers one by one and capture the varyings.
    for (uint32_t bufferIndex = 0; bufferIndex < mXfbVaryings.size(); ++bufferIndex)
    {
        spirv::IdRef xfbOffset(SpirvTransformerBase::GetNewId(blobOut));

        // Get the offset corresponding to this buffer:
        //
        //     int xfbOffset = xfbOffsets[N]
        spirv::WriteCompositeExtract(blobOut, ids.intId(), xfbOffset, xfbOffsets,
                                     {spirv::LiteralInteger(bufferIndex)});

        // Track offsets for verification.
        uint32_t offsetForVerification = 0;

        // Go over the varyings of this buffer in order.
        const std::vector<XfbVarying> &varyings = mXfbVaryings[bufferIndex];
        for (size_t varyingIndex = 0; varyingIndex < varyings.size(); ++varyingIndex)
        {
            const XfbVarying &varying                  = varyings[varyingIndex];
            const ShaderInterfaceVariableXfbInfo *info = varying.info;
            ASSERT(info->buffer == bufferIndex);

            // Each component of the varying being captured is loaded one by one.  This uses the
            // OpAccessChain instruction that takes a chain of "indices" to end up with the
            // component starting from the base variable.  For example:
            //
            //     var.member[3][2][0]
            //
            // where member is field number 4 in var and is a mat4, the access chain would be:
            //
            //     4 3 2 0
            //     ^ ^ ^ ^
            //     | | | |
            //     | | | row 0
            //     | | column 2
            //     | array element 3
            //     field 4
            //
            // The following tracks the access chain as the field, array elements, columns and rows
            // are looped over.
            spirv::IdRefList indexList;
            AccessChainIndexListAppend appendField(
                varying.fieldIndex != ShaderInterfaceVariableXfbInfo::kInvalid, mIntNIds,
                varying.fieldIndex, &indexList);

            // The varying being captured is either:
            //
            // - Not an array: In this case, no entry is added in the access chain
            // - An element of the array
            // - The whole array
            //
            uint32_t arrayIndexStart = 0;
            uint32_t arrayIndexEnd   = info->arraySize;
            const bool isArray       = info->arraySize != ShaderInterfaceVariableXfbInfo::kInvalid;
            if (varying.info->arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid)
            {
                // Capturing a single element.
                arrayIndexStart = varying.info->arrayIndex;
                arrayIndexEnd   = arrayIndexStart + 1;
            }
            else if (!isArray)
            {
                // Not an array.
                arrayIndexEnd = 1;
            }

            // Sorting the varyings should have ensured that offsets are in order and that writing
            // to the output buffer sequentially ends up using the correct offsets.
            ASSERT(info->offset == offsetForVerification);
            offsetForVerification += (arrayIndexEnd - arrayIndexStart) * info->rowCount *
                                     info->columnCount * sizeof(float);

            // Determine the type of the component being captured.  OpBitcast is used (the
            // implementation of intBitsToFloat() and uintBitsToFloat() for non-float types).
            spirv::IdRef varyingTypeId;
            spirv::IdRef varyingTypePtr;
            const bool isPrivate = varyingPrecisionFixer.isReplaced(varying.baseId);
            getVaryingTypeIds(ids, info->componentType, isPrivate, &varyingTypeId, &varyingTypePtr);

            for (uint32_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
            {
                AccessChainIndexListAppend appendArrayIndex(isArray, mIntNIds, arrayIndex,
                                                            &indexList);
                for (uint32_t col = 0; col < info->columnCount; ++col)
                {
                    AccessChainIndexListAppend appendColumn(info->columnCount > 1, mIntNIds, col,
                                                            &indexList);
                    for (uint32_t row = 0; row < info->rowCount; ++row)
                    {
                        AccessChainIndexListAppend appendRow(info->rowCount > 1, mIntNIds, row,
                                                             &indexList);

                        // Generate the code to capture a single component of the varying:
                        //
                        //     ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
                        writeComponentCapture(ids, bufferIndex, xfbOffset, varyingTypeId,
                                              varyingTypePtr, varying.baseId, indexList,
                                              info->componentType, blobOut);

                        // Increment the offset:
                        //
                        //     xfbOffset += 1;
                        //
                        // which translates to:
                        //
                        //     %newOffsetId = OpIAdd %int %currentOffsetId %int1
                        spirv::IdRef nextOffset(SpirvTransformerBase::GetNewId(blobOut));
                        spirv::WriteIAdd(blobOut, ids.intId(), nextOffset, xfbOffset, mIntNIds[1]);
                        xfbOffset = nextOffset;
                    }
                }
            }
        }
    }
}

void SpirvTransformFeedbackCodeGenerator::getVaryingTypeIds(const SpirvIDDiscoverer &ids,
                                                            GLenum componentType,
                                                            bool isPrivate,
                                                            spirv::IdRef *typeIdOut,
                                                            spirv::IdRef *typePtrOut)
{
    switch (componentType)
    {
        case GL_INT:
            *typeIdOut  = ids.intId();
            *typePtrOut = isPrivate ? mIntPrivatePointerId : mIntOutputPointerId;
            break;
        case GL_UNSIGNED_INT:
            *typeIdOut  = ids.uintId();
            *typePtrOut = isPrivate ? mUintPrivatePointerId : mUintOutputPointerId;
            break;
        case GL_FLOAT:
            *typeIdOut  = ids.floatId();
            *typePtrOut = isPrivate ? mFloatPrivatePointerId : mFloatOutputPointerId;
            break;
        default:
            UNREACHABLE();
    }

    ASSERT(typeIdOut->valid());
    ASSERT(typePtrOut->valid());
}

void SpirvTransformFeedbackCodeGenerator::writeGetOffsetsCall(spirv::IdRef xfbOffsets,
                                                              spirv::Blob *blobOut)
{
    const spirv::IdRef xfbGetOffsetsParam(SpirvTransformerBase::GetNewId(blobOut));
    const spirv::IdRef xfbOffsetsResult(SpirvTransformerBase::GetNewId(blobOut));
    const spirv::IdRef xfbOffsetsVar(SpirvTransformerBase::GetNewId(blobOut));

    // Generate code for the following:
    //
    //     ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));

    // Create a variable to hold the parameter, initialized with the constant ivec4 containing the
    // strides.
    spirv::WriteVariable(blobOut, mIVec4FuncPointerId, xfbGetOffsetsParam,
                         spv::StorageClassFunction, &mBufferStridesCompositeId);
    // Create a variable to hold the result.
    spirv::WriteVariable(blobOut, mIVec4FuncPointerId, xfbOffsetsVar, spv::StorageClassFunction,
                         nullptr);
    // Call a helper function generated by the translator to calculate the offsets for the current
    // vertex.
    spirv::WriteFunctionCall(blobOut, mIVec4Id, xfbOffsetsResult, mGetXfbOffsetsFuncId,
                             {xfbGetOffsetsParam});
    // Store the results.
    spirv::WriteStore(blobOut, xfbOffsetsVar, xfbOffsetsResult, nullptr);
    // Load from the variable for use in expressions.
    spirv::WriteLoad(blobOut, mIVec4Id, xfbOffsets, xfbOffsetsVar, nullptr);
}

void SpirvTransformFeedbackCodeGenerator::writeComponentCapture(
    const SpirvIDDiscoverer &ids,
    uint32_t bufferIndex,
    spirv::IdRef xfbOffset,
    spirv::IdRef varyingTypeId,
    spirv::IdRef varyingTypePtr,
    spirv::IdRef varyingBaseId,
    const spirv::IdRefList &accessChainIndices,
    GLenum componentType,
    spirv::Blob *blobOut)
{
    spirv::IdRef component(SpirvTransformerBase::GetNewId(blobOut));
    spirv::IdRef xfbOutPtr(SpirvTransformerBase::GetNewId(blobOut));

    // Generate code for the following:
    //
    //     ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]

    // Load from the component traversing the base variable with the given indices.  If there are no
    // indices, the variable can be loaded directly.
    spirv::IdRef loadPtr = varyingBaseId;
    if (!accessChainIndices.empty())
    {
        loadPtr = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteAccessChain(blobOut, varyingTypePtr, loadPtr, varyingBaseId,
                                accessChainIndices);
    }
    spirv::WriteLoad(blobOut, varyingTypeId, component, loadPtr, nullptr);

    // If the varying is int or uint, bitcast it to float to store in the float[] array used to
    // capture transform feedback output.
    spirv::IdRef asFloat = component;
    if (componentType != GL_FLOAT)
    {
        asFloat = SpirvTransformerBase::GetNewId(blobOut);
        spirv::WriteBitcast(blobOut, ids.floatId(), asFloat, component);
    }

    // Store into the transform feedback capture buffer at the current offset.  Note that this
    // buffer has only one field (xfbOut), hence ANGLEXfbN.xfbOut[xfbOffset] translates to ANGLEXfbN
    // with access chain {0, xfbOffset}.
    spirv::WriteAccessChain(blobOut, mFloatUniformPointerId, xfbOutPtr, mXfbBuffers[bufferIndex],
                            {mIntNIds[0], xfbOffset});
    spirv::WriteStore(blobOut, xfbOutPtr, asFloat, nullptr);
}

void SpirvTransformFeedbackCodeGenerator::addExecutionMode(spirv::IdRef entryPointId,
                                                           spirv::Blob *blobOut)
{
    if (mIsEmulated)
    {
        return;
    }

    if (mHasTransformFeedbackOutput)
    {
        spirv::WriteExecutionMode(blobOut, entryPointId, spv::ExecutionModeXfb);
    }
}

void SpirvTransformFeedbackCodeGenerator::addMemberDecorate(const ShaderInterfaceVariableInfo &info,
                                                            spirv::IdRef id,
                                                            spirv::Blob *blobOut)
{
    if (mIsEmulated || info.fieldXfb.empty())
    {
        return;
    }

    for (uint32_t fieldIndex = 0; fieldIndex < info.fieldXfb.size(); ++fieldIndex)
    {
        const ShaderInterfaceVariableXfbInfo &xfb = info.fieldXfb[fieldIndex];

        if (xfb.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
        {
            continue;
        }

        ASSERT(xfb.stride != ShaderInterfaceVariableXfbInfo::kInvalid);
        ASSERT(xfb.offset != ShaderInterfaceVariableXfbInfo::kInvalid);

        const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
            xfb.buffer,
            xfb.stride,
            xfb.offset,
        };

        // Generate the following three instructions:
        //
        //     OpMemberDecorate %id fieldIndex XfbBuffer xfb.buffer
        //     OpMemberDecorate %id fieldIndex XfbStride xfb.stride
        //     OpMemberDecorate %id fieldIndex Offset xfb.offset
        for (size_t i = 0; i < kXfbDecorationCount; ++i)
        {
            spirv::WriteMemberDecorate(blobOut, id, spirv::LiteralInteger(fieldIndex),
                                       kXfbDecorations[i],
                                       {spirv::LiteralInteger(xfbDecorationValues[i])});
        }
    }
}

void SpirvTransformFeedbackCodeGenerator::addDecorate(const ShaderInterfaceVariableInfo &info,
                                                      spirv::IdRef id,
                                                      spirv::Blob *blobOut)
{
    if (mIsEmulated || info.xfb.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
    {
        return;
    }

    ASSERT(info.xfb.stride != ShaderInterfaceVariableXfbInfo::kInvalid);
    ASSERT(info.xfb.offset != ShaderInterfaceVariableXfbInfo::kInvalid);

    const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
        info.xfb.buffer,
        info.xfb.stride,
        info.xfb.offset,
    };

    // Generate the following three instructions:
    //
    //     OpDecorate %id XfbBuffer xfb.buffer
    //     OpDecorate %id XfbStride xfb.stride
    //     OpDecorate %id Offset xfb.offset
    for (size_t i = 0; i < kXfbDecorationCount; ++i)
    {
        spirv::WriteDecorate(blobOut, id, kXfbDecorations[i],
                             {spirv::LiteralInteger(xfbDecorationValues[i])});
    }
}

// Helper class that generates code for gl_Position transformations
class SpirvPositionTransformer final : angle::NonCopyable
{
  public:
    SpirvPositionTransformer(const GlslangSpirvOptions &options) : mOptions(options) {}

    void writePositionTransformation(const SpirvIDDiscoverer &ids,
                                     spirv::IdRef positionPointerId,
                                     spirv::IdRef positionId,
                                     spirv::Blob *blobOut);

  private:
    void preRotateXY(const SpirvIDDiscoverer &ids,
                     spirv::IdRef xId,
                     spirv::IdRef yId,
                     spirv::IdRef *rotatedXIdOut,
                     spirv::IdRef *rotatedYIdOut,
                     spirv::Blob *blobOut);
    void transformZToVulkanClipSpace(const SpirvIDDiscoverer &ids,
                                     spirv::IdRef zId,
                                     spirv::IdRef wId,
                                     spirv::IdRef *correctedZIdOut,
                                     spirv::Blob *blobOut);

    GlslangSpirvOptions mOptions;
};

void SpirvPositionTransformer::writePositionTransformation(const SpirvIDDiscoverer &ids,
                                                           spirv::IdRef positionPointerId,
                                                           spirv::IdRef positionId,
                                                           spirv::Blob *blobOut)
{
    // In GL the viewport transformation is slightly different - see the GL 2.0 spec section "2.12.1
    // Controlling the Viewport".  In Vulkan the corresponding spec section is currently "23.4.
    // Coordinate Transformations".  The following transformation needs to be done:
    //
    //     z_vk = 0.5 * (w_gl + z_gl)
    //
    // where z_vk is the depth output of a Vulkan geometry-stage shader and z_gl is the same for GL.

    // Generate the following SPIR-V for prerotation and depth transformation:
    //
    //     // Create gl_Position.x and gl_Position.y for transformation, as well as gl_Position.z
    //     // and gl_Position.w for later.
    //     %x = OpCompositeExtract %mFloatId %Position 0
    //     %y = OpCompositeExtract %mFloatId %Position 1
    //     %z = OpCompositeExtract %mFloatId %Position 2
    //     %w = OpCompositeExtract %mFloatId %Position 3
    //
    //     // Transform %x and %y based on pre-rotation.  This could include swapping the two ids
    //     // (in the transformer, no need to generate SPIR-V instructions for that), and/or
    //     // negating either component.  To negate a component, the following instruction is used:
    //     (optional:) %negated = OpFNegate %mFloatId %component
    //
    //     // Transform %z if necessary, based on the above formula.
    //     %zPlusW = OpFAdd %mFloatId %z %w
    //     %correctedZ = OpFMul %mFloatId %zPlusW %mFloatHalfId
    //
    //     // Create the rotated gl_Position from the rotated x and y and corrected z components.
    //     %RotatedPosition = OpCompositeConstruct %mVec4Id %rotatedX %rotatedY %correctedZ %w
    //     // Store the results back in gl_Position
    //     OpStore %PositionPointer %RotatedPosition
    //
    const spirv::IdRef xId(SpirvTransformerBase::GetNewId(blobOut));
    const spirv::IdRef yId(SpirvTransformerBase::GetNewId(blobOut));
    const spirv::IdRef zId(SpirvTransformerBase::GetNewId(blobOut));
    const spirv::IdRef wId(SpirvTransformerBase::GetNewId(blobOut));
    const spirv::IdRef rotatedPositionId(SpirvTransformerBase::GetNewId(blobOut));

    spirv::WriteCompositeExtract(blobOut, ids.floatId(), xId, positionId,
                                 {spirv::LiteralInteger{0}});
    spirv::WriteCompositeExtract(blobOut, ids.floatId(), yId, positionId,
                                 {spirv::LiteralInteger{1}});
    spirv::WriteCompositeExtract(blobOut, ids.floatId(), zId, positionId,
                                 {spirv::LiteralInteger{2}});
    spirv::WriteCompositeExtract(blobOut, ids.floatId(), wId, positionId,
                                 {spirv::LiteralInteger{3}});

    spirv::IdRef rotatedXId;
    spirv::IdRef rotatedYId;
    preRotateXY(ids, xId, yId, &rotatedXId, &rotatedYId, blobOut);

    spirv::IdRef correctedZId;
    transformZToVulkanClipSpace(ids, zId, wId, &correctedZId, blobOut);

    spirv::WriteCompositeConstruct(blobOut, ids.vec4Id(), rotatedPositionId,
                                   {rotatedXId, rotatedYId, correctedZId, wId});
    spirv::WriteStore(blobOut, positionPointerId, rotatedPositionId, nullptr);
}

void SpirvPositionTransformer::preRotateXY(const SpirvIDDiscoverer &ids,
                                           spirv::IdRef xId,
                                           spirv::IdRef yId,
                                           spirv::IdRef *rotatedXIdOut,
                                           spirv::IdRef *rotatedYIdOut,
                                           spirv::Blob *blobOut)
{
    switch (mOptions.preRotation)
    {
        case SurfaceRotation::Identity:
            // [ 1  0]   [x]
            // [ 0  1] * [y]
            *rotatedXIdOut = xId;
            *rotatedYIdOut = yId;
            break;
        case SurfaceRotation::FlippedIdentity:
            if (mOptions.negativeViewportSupported)
            {
                // [ 1  0]   [x]
                // [ 0  1] * [y]
                *rotatedXIdOut = xId;
                *rotatedYIdOut = yId;
            }
            else
            {
                // [ 1  0]   [x]
                // [ 0 -1] * [y]
                *rotatedXIdOut = xId;
                *rotatedYIdOut = SpirvTransformerBase::GetNewId(blobOut);
                spirv::WriteFNegate(blobOut, ids.floatId(), *rotatedYIdOut, yId);
            }
            break;
        case SurfaceRotation::Rotated90Degrees:
        case SurfaceRotation::FlippedRotated90Degrees:
            // [ 0  1]   [x]
            // [-1  0] * [y]
            *rotatedXIdOut = yId;
            *rotatedYIdOut = SpirvTransformerBase::GetNewId(blobOut);
            spirv::WriteFNegate(blobOut, ids.floatId(), *rotatedYIdOut, xId);
            break;
        case SurfaceRotation::Rotated180Degrees:
        case SurfaceRotation::FlippedRotated180Degrees:
            // [-1  0]   [x]
            // [ 0 -1] * [y]
            *rotatedXIdOut = SpirvTransformerBase::GetNewId(blobOut);
            *rotatedYIdOut = SpirvTransformerBase::GetNewId(blobOut);
            spirv::WriteFNegate(blobOut, ids.floatId(), *rotatedXIdOut, xId);
            spirv::WriteFNegate(blobOut, ids.floatId(), *rotatedYIdOut, yId);
            break;
        case SurfaceRotation::Rotated270Degrees:
        case SurfaceRotation::FlippedRotated270Degrees:
            // [ 0 -1]   [x]
            // [ 1  0] * [y]
            *rotatedXIdOut = SpirvTransformerBase::GetNewId(blobOut);
            *rotatedYIdOut = xId;
            spirv::WriteFNegate(blobOut, ids.floatId(), *rotatedXIdOut, yId);
            break;
        default:
            UNREACHABLE();
    }
}

void SpirvPositionTransformer::transformZToVulkanClipSpace(const SpirvIDDiscoverer &ids,
                                                           spirv::IdRef zId,
                                                           spirv::IdRef wId,
                                                           spirv::IdRef *correctedZIdOut,
                                                           spirv::Blob *blobOut)
{
    if (!mOptions.transformPositionToVulkanClipSpace)
    {
        *correctedZIdOut = zId;
        return;
    }

    const spirv::IdRef zPlusWId(SpirvTransformerBase::GetNewId(blobOut));
    *correctedZIdOut = SpirvTransformerBase::GetNewId(blobOut);

    // %zPlusW = OpFAdd %mFloatId %z %w
    spirv::WriteFAdd(blobOut, ids.floatId(), zPlusWId, zId, wId);

    // %correctedZ = OpFMul %mFloatId %zPlusW %mFloatHalfId
    spirv::WriteFMul(blobOut, ids.floatId(), *correctedZIdOut, zPlusWId, ids.floatHalfId());
}

// A SPIR-V transformer.  It walks the instructions and modifies them as necessary, for example to
// assign bindings or locations.
class SpirvTransformer final : public SpirvTransformerBase
{
  public:
    SpirvTransformer(const spirv::Blob &spirvBlobIn,
                     const GlslangSpirvOptions &options,
                     const ShaderInterfaceVariableInfoMap &variableInfoMap,
                     spirv::Blob *spirvBlobOut)
        : SpirvTransformerBase(spirvBlobIn, variableInfoMap, spirvBlobOut),
          mOptions(options),
          mXfbCodeGenerator(options.isTransformFeedbackEmulated),
          mPositionTransformer(options)
    {}

    bool transform();

  private:
    // A prepass to resolve interesting ids:
    void resolveVariableIds();

    // Transform instructions:
    void transformInstruction();

    // Instructions that are purely informational:
    void visitDecorate(const uint32_t *instruction);
    void visitName(const uint32_t *instruction);
    void visitMemberName(const uint32_t *instruction);
    void visitTypeArray(const uint32_t *instruction);
    void visitTypeFloat(const uint32_t *instruction);
    void visitTypeInt(const uint32_t *instruction);
    void visitTypePointer(const uint32_t *instruction);
    void visitTypeVector(const uint32_t *instruction);
    void visitVariable(const uint32_t *instruction);

    // Instructions that potentially need transformation.  They return true if the instruction is
    // transformed.  If false is returned, the instruction should be copied as-is.
    TransformationState transformAccessChain(const uint32_t *instruction);
    TransformationState transformCapability(const uint32_t *instruction);
    TransformationState transformDebugInfo(const uint32_t *instruction, spv::Op op);
    TransformationState transformEmitVertex(const uint32_t *instruction);
    TransformationState transformEntryPoint(const uint32_t *instruction);
    TransformationState transformDecorate(const uint32_t *instruction);
    TransformationState transformMemberDecorate(const uint32_t *instruction);
    TransformationState transformTypePointer(const uint32_t *instruction);
    TransformationState transformTypeStruct(const uint32_t *instruction);
    TransformationState transformReturn(const uint32_t *instruction);
    TransformationState transformVariable(const uint32_t *instruction);
    TransformationState transformExecutionMode(const uint32_t *instruction);

    // Helpers:
    void visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId);
    void writePendingDeclarations();
    void writeInputPreamble();
    void writeOutputPrologue();

    // Special flags:
    GlslangSpirvOptions mOptions;

    // Traversal state:
    bool mInsertFunctionVariables = false;
    spirv::IdRef mEntryPointId;
    spirv::IdRef mCurrentFunctionId;

    SpirvIDDiscoverer mIds;

    // Transformation state:

    SpirvPerVertexTrimmer mPerVertexTrimmer;
    SpirvInactiveVaryingRemover mInactiveVaryingRemover;
    SpirvVaryingPrecisionFixer mVaryingPrecisionFixer;
    SpirvTransformFeedbackCodeGenerator mXfbCodeGenerator;
    SpirvPositionTransformer mPositionTransformer;
};

bool SpirvTransformer::transform()
{
    onTransformBegin();

    // First, find all necessary ids and associate them with the information required to transform
    // their decorations.
    resolveVariableIds();

    while (mCurrentWord < mSpirvBlobIn.size())
    {
        transformInstruction();
    }

    return true;
}

void SpirvTransformer::resolveVariableIds()
{
    const size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound];

    mIds.init(indexBound);
    mInactiveVaryingRemover.init(indexBound);
    mVaryingPrecisionFixer.init(indexBound);

    // Allocate storage for id-to-info map.  If %i is the id of a name in mVariableInfoMap, index i
    // in this vector will hold a pointer to the ShaderInterfaceVariableInfo object associated with
    // that name in mVariableInfoMap.
    mVariableInfoById.resize(indexBound, nullptr);

    size_t currentWord = kHeaderIndexInstructions;

    while (currentWord < mSpirvBlobIn.size())
    {
        const uint32_t *instruction = &mSpirvBlobIn[currentWord];

        uint32_t wordCount;
        spv::Op opCode;
        spirv::GetInstructionOpAndLength(instruction, &opCode, &wordCount);

        switch (opCode)
        {
            case spv::OpDecorate:
                visitDecorate(instruction);
                break;
            case spv::OpName:
                visitName(instruction);
                break;
            case spv::OpMemberName:
                visitMemberName(instruction);
                break;
            case spv::OpTypeArray:
                visitTypeArray(instruction);
                break;
            case spv::OpTypeFloat:
                visitTypeFloat(instruction);
                break;
            case spv::OpTypeInt:
                visitTypeInt(instruction);
                break;
            case spv::OpTypePointer:
                visitTypePointer(instruction);
                break;
            case spv::OpTypeVector:
                visitTypeVector(instruction);
                break;
            case spv::OpVariable:
                visitVariable(instruction);
                break;
            case spv::OpFunction:
                // SPIR-V is structured in sections (SPIR-V 1.0 Section 2.4 Logical Layout of a
                // Module). Names appear before decorations, which are followed by type+variables
                // and finally functions.  We are only interested in name and variable declarations
                // (as well as type declarations for the sake of nameless interface blocks).  Early
                // out when the function declaration section is met.
                return;
            default:
                break;
        }

        currentWord += wordCount;
    }
    UNREACHABLE();
}

void SpirvTransformer::transformInstruction()
{
    uint32_t wordCount;
    spv::Op opCode;
    const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);

    if (opCode == spv::OpFunction)
    {
        spirv::IdResultType id;
        spv::FunctionControlMask functionControl;
        spirv::IdRef functionType;
        spirv::ParseFunction(instruction, &id, &mCurrentFunctionId, &functionControl,
                             &functionType);

        // SPIR-V is structured in sections.  Function declarations come last.  Only a few
        // instructions such as Op*Access* or OpEmitVertex opcodes inside functions need to be
        // inspected.
        //
        // If this is the first OpFunction instruction, this is also where the declaration section
        // finishes, so we need to declare anything that we need but didn't find there already right
        // now.
        if (!mIsInFunctionSection)
        {
            writePendingDeclarations();
        }
        mIsInFunctionSection = true;

        // Only write function variables for the EntryPoint function for non-compute shaders
        mInsertFunctionVariables =
            mCurrentFunctionId == mEntryPointId && mOptions.shaderType != gl::ShaderType::Compute;
    }

    // Only look at interesting instructions.
    TransformationState transformationState = TransformationState::Unchanged;

    if (mIsInFunctionSection)
    {
        // After we process an OpFunction instruction and any instructions that must come
        // immediately after OpFunction we need to check if there are any precision mismatches that
        // need to be handled. If so, output OpVariable for each variable that needed to change from
        // a StorageClassOutput to a StorageClassFunction.
        if (mInsertFunctionVariables && opCode != spv::OpFunction &&
            opCode != spv::OpFunctionParameter && opCode != spv::OpLabel &&
            opCode != spv::OpVariable)
        {
            writeInputPreamble();
            mInsertFunctionVariables = false;
        }

        // Look at in-function opcodes.
        switch (opCode)
        {
            case spv::OpAccessChain:
            case spv::OpInBoundsAccessChain:
            case spv::OpPtrAccessChain:
            case spv::OpInBoundsPtrAccessChain:
                transformationState = transformAccessChain(instruction);
                break;

            case spv::OpEmitVertex:
                transformationState = transformEmitVertex(instruction);
                break;
            case spv::OpReturn:
                transformationState = transformReturn(instruction);
                break;
            default:
                break;
        }
    }
    else
    {
        // Look at global declaration opcodes.
        switch (opCode)
        {
            case spv::OpName:
            case spv::OpMemberName:
            case spv::OpString:
            case spv::OpLine:
            case spv::OpNoLine:
            case spv::OpModuleProcessed:
                transformationState = transformDebugInfo(instruction, opCode);
                break;
            case spv::OpCapability:
                transformationState = transformCapability(instruction);
                break;
            case spv::OpEntryPoint:
                transformationState = transformEntryPoint(instruction);
                break;
            case spv::OpDecorate:
                transformationState = transformDecorate(instruction);
                break;
            case spv::OpMemberDecorate:
                transformationState = transformMemberDecorate(instruction);
                break;
            case spv::OpTypePointer:
                transformationState = transformTypePointer(instruction);
                break;
            case spv::OpTypeStruct:
                transformationState = transformTypeStruct(instruction);
                break;
            case spv::OpVariable:
                transformationState = transformVariable(instruction);
                break;
            case spv::OpExecutionMode:
                transformationState = transformExecutionMode(instruction);
                break;
            default:
                break;
        }
    }

    // If the instruction was not transformed, copy it to output as is.
    if (transformationState == TransformationState::Unchanged)
    {
        copyInstruction(instruction, wordCount);
    }

    // Advance to next instruction.
    mCurrentWord += wordCount;
}

// Called at the end of the declarations section.  Any declarations that are necessary but weren't
// present in the original shader need to be done here.
void SpirvTransformer::writePendingDeclarations()
{
    // Pre-rotation and transformation of depth to Vulkan clip space require declarations that may
    // not necessarily be in the shader.  Transform feedback emulation additionally requires a few
    // overlapping ids.
    if (IsRotationIdentity(mOptions.preRotation) && !mOptions.transformPositionToVulkanClipSpace &&
        !mOptions.isTransformFeedbackStage)
    {
        return;
    }

    mIds.writePendingDeclarations(mSpirvBlobOut);
    mXfbCodeGenerator.writePendingDeclarations(mVariableInfoById, mIds, mSpirvBlobOut);
}

// Called by transformInstruction to insert necessary instructions for casting varyings.
void SpirvTransformer::writeInputPreamble()
{
    mVaryingPrecisionFixer.writeInputPreamble(mVariableInfoById, mOptions.shaderType,
                                              mSpirvBlobOut);
}

// Called by transformInstruction to insert necessary instructions for casting varyings and
// modifying gl_Position.
void SpirvTransformer::writeOutputPrologue()
{
    mVaryingPrecisionFixer.writeOutputPrologue(mVariableInfoById, mOptions.shaderType,
                                               mSpirvBlobOut);

    if (!mIds.outputPerVertexId().valid())
    {
        return;
    }

    // Whether gl_Position should be transformed to account for prerotation and Vulkan clip space.
    const bool transformPosition =
        !IsRotationIdentity(mOptions.preRotation) || mOptions.transformPositionToVulkanClipSpace;
    const bool isXfbExtensionStage =
        mOptions.isTransformFeedbackStage && !mOptions.isTransformFeedbackEmulated;
    if (!transformPosition && !isXfbExtensionStage)
    {
        return;
    }

    // Load gl_Position with the following SPIR-V:
    //
    //     // Create an access chain to gl_PerVertex.gl_Position, which is always at index 0.
    //     %PositionPointer = OpAccessChain %mVec4OutTypePointerId %mOutputPerVertexId %mInt0Id
    //     // Load gl_Position
    //     %Position = OpLoad %mVec4Id %PositionPointer
    //
    const spirv::IdRef positionPointerId(getNewId());
    const spirv::IdRef positionId(getNewId());

    spirv::WriteAccessChain(mSpirvBlobOut, mIds.vec4OutTypePointerId(), positionPointerId,
                            mIds.outputPerVertexId(), {mIds.int0Id()});
    spirv::WriteLoad(mSpirvBlobOut, mIds.vec4Id(), positionId, positionPointerId, nullptr);

    // Write transform feedback output before modifying gl_Position.
    if (isXfbExtensionStage)
    {
        mXfbCodeGenerator.writeTransformFeedbackExtensionOutput(mIds, positionId, mSpirvBlobOut);
    }

    if (transformPosition)
    {
        mPositionTransformer.writePositionTransformation(mIds, positionPointerId, positionId,
                                                         mSpirvBlobOut);
    }
}

void SpirvTransformer::visitDecorate(const uint32_t *instruction)
{
    spirv::IdRef id;
    spv::Decoration decoration;
    spirv::ParseDecorate(instruction, &id, &decoration, nullptr);

    mIds.visitDecorate(id, decoration);

    if (mIds.isIOBlock(id))
    {
        // For I/O blocks, associate the type with the info, which is used to decorate its members
        // with transform feedback if any.
        spirv::LiteralString name = mIds.getName(id);
        ASSERT(name != nullptr);

        const ShaderInterfaceVariableInfo &info = mVariableInfoMap.get(mOptions.shaderType, name);
        mVariableInfoById[id]                   = &info;
    }
}

void SpirvTransformer::visitName(const uint32_t *instruction)
{
    spirv::IdRef id;
    spirv::LiteralString name;
    spirv::ParseName(instruction, &id, &name);

    mIds.visitName(id, name);
    mXfbCodeGenerator.visitName(id, name);
}

void SpirvTransformer::visitMemberName(const uint32_t *instruction)
{
    spirv::IdRef id;
    spirv::LiteralInteger member;
    spirv::LiteralString name;
    spirv::ParseMemberName(instruction, &id, &member, &name);

    if (!mVariableInfoMap.contains(mOptions.shaderType, name))
    {
        return;
    }

    const ShaderInterfaceVariableInfo &info = mVariableInfoMap.get(mOptions.shaderType, name);

    mIds.visitMemberName(info, id, member, name);
}

void SpirvTransformer::visitTypeArray(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::IdRef elementType;
    spirv::IdRef length;
    spirv::ParseTypeArray(instruction, &id, &elementType, &length);

    mIds.visitTypeArray(id, elementType, length);
}

void SpirvTransformer::visitTypeFloat(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::LiteralInteger width;
    spirv::ParseTypeFloat(instruction, &id, &width);

    mIds.visitTypeFloat(id, width);
}

void SpirvTransformer::visitTypeInt(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::LiteralInteger width;
    spirv::LiteralInteger signedness;
    spirv::ParseTypeInt(instruction, &id, &width, &signedness);

    mIds.visitTypeInt(id, width, signedness);
}

void SpirvTransformer::visitTypePointer(const uint32_t *instruction)
{
    spirv::IdResult id;
    spv::StorageClass storageClass;
    spirv::IdRef typeId;
    spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);

    mIds.visitTypePointer(id, storageClass, typeId);
    mVaryingPrecisionFixer.visitTypePointer(id, storageClass, typeId);
    mXfbCodeGenerator.visitTypePointer(id, storageClass, typeId);
}

void SpirvTransformer::visitTypeVector(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::IdRef componentId;
    spirv::LiteralInteger componentCount;
    spirv::ParseTypeVector(instruction, &id, &componentId, &componentCount);

    mIds.visitTypeVector(id, componentId, componentCount);
    mXfbCodeGenerator.visitTypeVector(mIds, id, componentId, componentCount);
}

void SpirvTransformer::visitVariable(const uint32_t *instruction)
{
    spirv::IdResultType typeId;
    spirv::IdResult id;
    spv::StorageClass storageClass;
    spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);

    spirv::LiteralString name;
    SpirvVariableType variableType = mIds.visitVariable(typeId, id, storageClass, &name);

    if (variableType == SpirvVariableType::Other)
    {
        return;
    }

    // The ids are unique.
    ASSERT(id < mVariableInfoById.size());
    ASSERT(mVariableInfoById[id] == nullptr);

    if (variableType == SpirvVariableType::BuiltIn)
    {
        // Make all builtins point to this no-op info.  Adding this entry allows us to ASSERT that
        // every shader interface variable is processed during the SPIR-V transformation.  This is
        // done when iterating the ids provided by OpEntryPoint.
        mVariableInfoById[id] = &mBuiltinVariableInfo;
        return;
    }

    // Every shader interface variable should have an associated data.
    const ShaderInterfaceVariableInfo &info = mVariableInfoMap.get(mOptions.shaderType, name);

    // Associate the id of this name with its info.
    mVariableInfoById[id] = &info;

    mVaryingPrecisionFixer.visitVariable(info, mOptions.shaderType, typeId, id, storageClass,
                                         mSpirvBlobOut);
    if (mOptions.isTransformFeedbackStage)
    {
        mXfbCodeGenerator.visitVariable(info, mOptions.shaderType, name, typeId, id, storageClass);
    }
}

TransformationState SpirvTransformer::transformDecorate(const uint32_t *instruction)
{
    spirv::IdRef id;
    spv::Decoration decoration;
    spirv::LiteralIntegerList decorationValues;
    spirv::ParseDecorate(instruction, &id, &decoration, &decorationValues);

    ASSERT(id < mVariableInfoById.size());
    const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];

    // If variable is not a shader interface variable that needs modification, there's nothing to
    // do.
    if (info == nullptr)
    {
        return TransformationState::Unchanged;
    }

    if (mInactiveVaryingRemover.transformDecorate(*info, mOptions.shaderType, id, decoration,
                                                  decorationValues, mSpirvBlobOut) ==
        TransformationState::Transformed)
    {
        return TransformationState::Transformed;
    }

    // If using relaxed precision, generate instructions for the replacement id instead.
    id = mVaryingPrecisionFixer.getReplacementId(id);

    uint32_t newDecorationValue = ShaderInterfaceVariableInfo::kInvalid;

    switch (decoration)
    {
        case spv::DecorationLocation:
            newDecorationValue = info->location;
            break;
        case spv::DecorationBinding:
            newDecorationValue = info->binding;
            break;
        case spv::DecorationDescriptorSet:
            newDecorationValue = info->descriptorSet;
            break;
        case spv::DecorationFlat:
            if (info->useRelaxedPrecision)
            {
                // Change the id to replacement variable
                spirv::WriteDecorate(mSpirvBlobOut, id, decoration, decorationValues);
                return TransformationState::Transformed;
            }
            break;
        case spv::DecorationBlock:
            // If this is the Block decoration of a shader I/O block, add the transform feedback
            // decorations to its members right away.
            if (mOptions.isTransformFeedbackStage)
            {
                mXfbCodeGenerator.addMemberDecorate(*info, id, mSpirvBlobOut);
            }
            break;
        default:
            break;
    }

    // If the decoration is not something we care about modifying, there's nothing to do.
    if (newDecorationValue == ShaderInterfaceVariableInfo::kInvalid)
    {
        return TransformationState::Unchanged;
    }

    // Modify the decoration value.
    ASSERT(decorationValues.size() == 1);
    spirv::WriteDecorate(mSpirvBlobOut, id, decoration,
                         {spirv::LiteralInteger(newDecorationValue)});

    // If there are decorations to be added, add them right after the Location decoration is
    // encountered.
    if (decoration != spv::DecorationLocation)
    {
        return TransformationState::Transformed;
    }

    // If any, the replacement variable is always reduced precision so add that decoration to
    // fixedVaryingId.
    if (info->useRelaxedPrecision)
    {
        mVaryingPrecisionFixer.addDecorate(id, mSpirvBlobOut);
    }

    // Add component decoration, if any.
    if (info->component != ShaderInterfaceVariableInfo::kInvalid)
    {
        spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationComponent,
                             {spirv::LiteralInteger(info->component)});
    }

    // Add index decoration, if any.
    if (info->index != ShaderInterfaceVariableInfo::kInvalid)
    {
        spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationIndex,
                             {spirv::LiteralInteger(info->index)});
    }

    // Add Xfb decorations, if any.
    if (mOptions.isTransformFeedbackStage)
    {
        mXfbCodeGenerator.addDecorate(*info, id, mSpirvBlobOut);
    }

    return TransformationState::Transformed;
}

TransformationState SpirvTransformer::transformMemberDecorate(const uint32_t *instruction)
{
    spirv::IdRef typeId;
    spirv::LiteralInteger member;
    spv::Decoration decoration;
    spirv::ParseMemberDecorate(instruction, &typeId, &member, &decoration, nullptr);

    return mPerVertexTrimmer.transformMemberDecorate(mIds, typeId, member, decoration);
}

TransformationState SpirvTransformer::transformCapability(const uint32_t *instruction)
{
    spv::Capability capability;
    spirv::ParseCapability(instruction, &capability);

    return mXfbCodeGenerator.transformCapability(capability, mSpirvBlobOut);
}

TransformationState SpirvTransformer::transformDebugInfo(const uint32_t *instruction, spv::Op op)
{
    if (mOptions.removeDebugInfo)
    {
        // Strip debug info to reduce binary size.
        return TransformationState::Transformed;
    }

    // In the case of OpMemberName, unconditionally remove stripped gl_PerVertex members.
    if (op == spv::OpMemberName)
    {
        spirv::IdRef id;
        spirv::LiteralInteger member;
        spirv::LiteralString name;
        spirv::ParseMemberName(instruction, &id, &member, &name);

        return mPerVertexTrimmer.transformMemberName(mIds, id, member, name);
    }

    if (op == spv::OpName)
    {
        spirv::IdRef id;
        spirv::LiteralString name;
        spirv::ParseName(instruction, &id, &name);

        return mXfbCodeGenerator.transformName(id, name);
    }

    return TransformationState::Unchanged;
}

TransformationState SpirvTransformer::transformEmitVertex(const uint32_t *instruction)
{
    // This is only possible in geometry shaders.
    ASSERT(mOptions.shaderType == gl::ShaderType::Geometry);

    // Write the temporary variables that hold varyings data before EmitVertex().
    writeOutputPrologue();

    return TransformationState::Unchanged;
}

TransformationState SpirvTransformer::transformEntryPoint(const uint32_t *instruction)
{
    // Should only have one EntryPoint
    ASSERT(!mEntryPointId.valid());

    spv::ExecutionModel executionModel;
    spirv::LiteralString name;
    spirv::IdRefList interfaceList;
    spirv::ParseEntryPoint(instruction, &executionModel, &mEntryPointId, &name, &interfaceList);

    mInactiveVaryingRemover.modifyEntryPointInterfaceList(mVariableInfoById, mOptions.shaderType,
                                                          &interfaceList);
    mVaryingPrecisionFixer.modifyEntryPointInterfaceList(&interfaceList);

    // Write the entry point with the inactive interface variables removed.
    spirv::WriteEntryPoint(mSpirvBlobOut, executionModel, mEntryPointId, name, interfaceList);

    // Add an OpExecutionMode Xfb instruction if necessary.
    mXfbCodeGenerator.addExecutionMode(mEntryPointId, mSpirvBlobOut);

    return TransformationState::Transformed;
}

TransformationState SpirvTransformer::transformTypePointer(const uint32_t *instruction)
{
    spirv::IdResult id;
    spv::StorageClass storageClass;
    spirv::IdRef typeId;
    spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);

    return mInactiveVaryingRemover.transformTypePointer(mIds, id, storageClass, typeId,
                                                        mSpirvBlobOut);
}

TransformationState SpirvTransformer::transformTypeStruct(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::IdRefList memberList;
    ParseTypeStruct(instruction, &id, &memberList);

    return mPerVertexTrimmer.transformTypeStruct(mIds, id, &memberList, mSpirvBlobOut);
}

TransformationState SpirvTransformer::transformReturn(const uint32_t *instruction)
{
    if (mCurrentFunctionId != mEntryPointId)
    {
        if (mOptions.isTransformFeedbackStage)
        {
            // Transform feedback emulation is written to a designated function.  Allow its code to
            // be generated if this is the right function.
            mXfbCodeGenerator.writeTransformFeedbackEmulationOutput(
                mIds, mVaryingPrecisionFixer, mCurrentFunctionId, mSpirvBlobOut);
        }

        // We only need to process the precision info when returning from the entry point function
        return TransformationState::Unchanged;
    }

    // For geometry shaders, this operations is done before every EmitVertex() instead.
    // Additionally, this transformation (which affects output varyings) doesn't apply to fragment
    // shaders.
    if (mOptions.shaderType == gl::ShaderType::Geometry ||
        mOptions.shaderType == gl::ShaderType::Fragment)
    {
        return TransformationState::Unchanged;
    }

    writeOutputPrologue();

    return TransformationState::Unchanged;
}

TransformationState SpirvTransformer::transformVariable(const uint32_t *instruction)
{
    spirv::IdResultType typeId;
    spirv::IdResult id;
    spv::StorageClass storageClass;
    spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);

    const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];

    // If variable is not a shader interface variable that needs modification, there's nothing to
    // do.
    if (info == nullptr)
    {
        return TransformationState::Unchanged;
    }

    // Furthermore, if it's not an inactive varying output, there's nothing to do.  Note that
    // inactive varying inputs are already pruned by the translator.
    // However, input or output storage class for interface block will not be pruned when a shader
    // is compiled separately.
    if (info->activeStages[mOptions.shaderType])
    {
        if (mVaryingPrecisionFixer.transformVariable(
                *info, typeId, id, storageClass, mSpirvBlobOut) == TransformationState::Transformed)
        {
            // Make original variable a private global
            return mInactiveVaryingRemover.transformVariable(typeId, id, storageClass,
                                                             mSpirvBlobOut);
        }
        return TransformationState::Unchanged;
    }

    if (mXfbCodeGenerator.transformVariable(*info, mVariableInfoMap, mOptions.shaderType, typeId,
                                            id, storageClass) == TransformationState::Transformed)
    {
        return TransformationState::Transformed;
    }

    // The variable is inactive.  Output a modified variable declaration, where the type is the
    // corresponding type with the Private storage class.
    return mInactiveVaryingRemover.transformVariable(typeId, id, storageClass, mSpirvBlobOut);
}

TransformationState SpirvTransformer::transformAccessChain(const uint32_t *instruction)
{
    spirv::IdResultType typeId;
    spirv::IdResult id;
    spirv::IdRef baseId;
    spirv::IdRefList indexList;
    spirv::ParseAccessChain(instruction, &typeId, &id, &baseId, &indexList);

    // If not accessing an inactive output varying, nothing to do.
    const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
    if (info == nullptr)
    {
        return TransformationState::Unchanged;
    }

    if (info->activeStages[mOptions.shaderType] && !info->useRelaxedPrecision)
    {
        return TransformationState::Unchanged;
    }

    return mInactiveVaryingRemover.transformAccessChain(typeId, id, baseId, indexList,
                                                        mSpirvBlobOut);
}

TransformationState SpirvTransformer::transformExecutionMode(const uint32_t *instruction)
{
    spirv::IdRef entryPoint;
    spv::ExecutionMode mode;
    spirv::ParseExecutionMode(instruction, &entryPoint, &mode);

    if (mode == spv::ExecutionModeEarlyFragmentTests &&
        mOptions.removeEarlyFragmentTestsOptimization)
    {
        // Drop the instruction.
        return TransformationState::Transformed;
    }
    return TransformationState::Unchanged;
}

struct AliasingAttributeMap
{
    // The SPIR-V id of the aliasing attribute with the most components.  This attribute will be
    // used to read from this location instead of every aliasing one.
    spirv::IdRef attribute;

    // SPIR-V ids of aliasing attributes.
    std::vector<spirv::IdRef> aliasingAttributes;
};

void ValidateShaderInterfaceVariableIsAttribute(const ShaderInterfaceVariableInfo *info)
{
    ASSERT(info);
    ASSERT(info->activeStages[gl::ShaderType::Vertex]);
    ASSERT(info->attributeComponentCount > 0);
    ASSERT(info->attributeLocationCount > 0);
    ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);
}

void ValidateIsAliasingAttribute(const AliasingAttributeMap *aliasingMap, uint32_t id)
{
    ASSERT(id != aliasingMap->attribute);
    ASSERT(std::find(aliasingMap->aliasingAttributes.begin(), aliasingMap->aliasingAttributes.end(),
                     id) != aliasingMap->aliasingAttributes.end());
}

// A transformation that resolves vertex attribute aliases.  Note that vertex attribute aliasing is
// only allowed in GLSL ES 100, where the attribute types can only be one of float, vec2, vec3,
// vec4, mat2, mat3, and mat4.  Matrix attributes are handled by expanding them to multiple vector
// attributes, each occupying one location.
class SpirvVertexAttributeAliasingTransformer final : public SpirvTransformerBase
{
  public:
    SpirvVertexAttributeAliasingTransformer(
        const spirv::Blob &spirvBlobIn,
        const ShaderInterfaceVariableInfoMap &variableInfoMap,
        std::vector<const ShaderInterfaceVariableInfo *> &&variableInfoById,
        spirv::Blob *spirvBlobOut)
        : SpirvTransformerBase(spirvBlobIn, variableInfoMap, spirvBlobOut)
    {
        mVariableInfoById = std::move(variableInfoById);
    }

    bool transform();

  private:
    // Preprocess aliasing attributes in preparation for their removal.
    void preprocessAliasingAttributes();

    // Transform instructions:
    void transformInstruction();

    // Helpers:
    spirv::IdRef getAliasingAttributeReplacementId(spirv::IdRef aliasingId, uint32_t offset) const;
    bool isMatrixAttribute(spirv::IdRef id) const;

    // Instructions that are purely informational:
    void visitTypeFloat(const uint32_t *instruction);
    void visitTypeVector(const uint32_t *instruction);
    void visitTypeMatrix(const uint32_t *instruction);
    void visitTypePointer(const uint32_t *instruction);

    // Instructions that potentially need transformation.  They return true if the instruction is
    // transformed.  If false is returned, the instruction should be copied as-is.
    TransformationState transformEntryPoint(const uint32_t *instruction);
    TransformationState transformName(const uint32_t *instruction);
    TransformationState transformDecorate(const uint32_t *instruction);
    TransformationState transformVariable(const uint32_t *instruction);
    TransformationState transformAccessChain(const uint32_t *instruction);
    void transformLoadHelper(spirv::IdRef pointerId,
                             spirv::IdRef typeId,
                             spirv::IdRef replacementId,
                             spirv::IdRef resultId);
    TransformationState transformLoad(const uint32_t *instruction);

    void declareExpandedMatrixVectors();
    void writeExpandedMatrixInitialization();

    // Transformation state:

    // Map of aliasing attributes per location.
    gl::AttribArray<AliasingAttributeMap> mAliasingAttributeMap;

    // For each id, this map indicates whether it refers to an aliasing attribute that needs to be
    // removed.
    std::vector<bool> mIsAliasingAttributeById;

    // Matrix attributes are split into vectors, each occupying one location.  The SPIR-V
    // declaration would need to change from:
    //
    //     %type = OpTypeMatrix %vectorType N
    //     %matrixType = OpTypePointer Input %type
    //     %matrix = OpVariable %matrixType Input
    //
    // to:
    //
    //     %matrixType = OpTypePointer Private %type
    //     %matrix = OpVariable %matrixType Private
    //
    //     %vecType = OpTypePointer Input %vectorType
    //
    //     %vec0 = OpVariable %vecType Input
    //     ...
    //     %vecN-1 = OpVariable %vecType Input
    //
    // For each id %matrix (which corresponds to a matrix attribute), this map contains %vec0.  The
    // ids of the split vectors are consecutive, so %veci == %vec0 + i.  %veciType is taken from
    // mInputTypePointers.
    std::vector<spirv::IdRef> mExpandedMatrixFirstVectorIdById;
    // Whether the expanded matrix OpVariables are generated.
    bool mHaveMatricesExpanded = false;
    // Whether initialization of the matrix attributes should be written at the beginning of the
    // current function.
    bool mWriteExpandedMatrixInitialization = false;
    spirv::IdRef mEntryPointId;

    // Id of attribute types; float and veci.  This array is one-based, and [0] is unused.
    //
    // [1]: id of OpTypeFloat 32
    // [N]: id of OpTypeVector %[1] N, N = {2, 3, 4}
    //
    // In other words, index of the array corresponds to the number of components in the type.
    std::array<spirv::IdRef, 5> mFloatTypes;

    // Corresponding to mFloatTypes, [i]: id of OpMatrix %mFloatTypes[i] i.  Note that only square
    // matrices are possible as attributes in GLSL ES 1.00.  [0] and [1] are unused.
    std::array<spirv::IdRef, 5> mMatrixTypes;

    // Corresponding to mFloatTypes, [i]: id of OpTypePointer Input %mFloatTypes[i].  [0] is unused.
    std::array<spirv::IdRef, 5> mInputTypePointers;

    // Corresponding to mFloatTypes, [i]: id of OpTypePointer Private %mFloatTypes[i].  [0] is
    // unused.
    std::array<spirv::IdRef, 5> mPrivateFloatTypePointers;

    // Corresponding to mMatrixTypes, [i]: id of OpTypePointer Private %mMatrixTypes[i].  [0] and
    // [1] are unused.
    std::array<spirv::IdRef, 5> mPrivateMatrixTypePointers;
};

bool SpirvVertexAttributeAliasingTransformer::transform()
{
    onTransformBegin();

    preprocessAliasingAttributes();

    while (mCurrentWord < mSpirvBlobIn.size())
    {
        transformInstruction();
    }

    return true;
}

void SpirvVertexAttributeAliasingTransformer::preprocessAliasingAttributes()
{
    const size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound];

    mVariableInfoById.resize(indexBound, nullptr);
    mIsAliasingAttributeById.resize(indexBound, false);
    mExpandedMatrixFirstVectorIdById.resize(indexBound);

    // Go through attributes and find out which alias which.
    for (size_t idIndex = spirv::kMinValidId; idIndex < indexBound; ++idIndex)
    {
        const spirv::IdRef id(idIndex);

        const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];

        // Ignore non attribute ids.
        if (info == nullptr || info->attributeComponentCount == 0)
        {
            continue;
        }

        ASSERT(info->activeStages[gl::ShaderType::Vertex]);
        ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);

        const bool isMatrixAttribute = info->attributeLocationCount > 1;

        for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
        {
            uint32_t location = info->location + offset;
            ASSERT(location < mAliasingAttributeMap.size());

            spirv::IdRef attributeId(id);

            // If this is a matrix attribute, expand it to vectors.
            if (isMatrixAttribute)
            {
                const spirv::IdRef matrixId(id);

                // Get a new id for this location and associate it with the matrix.
                attributeId = getNewId();
                if (offset == 0)
                {
                    mExpandedMatrixFirstVectorIdById[matrixId] = attributeId;
                }
                // The ids are consecutive.
                ASSERT(attributeId == mExpandedMatrixFirstVectorIdById[matrixId] + offset);

                mIsAliasingAttributeById.resize(attributeId + 1, false);
                mVariableInfoById.resize(attributeId + 1, nullptr);
                mVariableInfoById[attributeId] = info;
            }

            AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[location];

            // If this is the first attribute in this location, remember it.
            if (!aliasingMap->attribute.valid())
            {
                aliasingMap->attribute = attributeId;
                continue;
            }

            // Otherwise, either add it to the list of aliasing attributes, or replace the main
            // attribute (and add that to the list of aliasing attributes).  The one with the
            // largest number of components is used as the main attribute.
            const ShaderInterfaceVariableInfo *curMainAttribute =
                mVariableInfoById[aliasingMap->attribute];
            ASSERT(curMainAttribute != nullptr && curMainAttribute->attributeComponentCount > 0);

            spirv::IdRef aliasingId;
            if (info->attributeComponentCount > curMainAttribute->attributeComponentCount)
            {
                aliasingId             = aliasingMap->attribute;
                aliasingMap->attribute = attributeId;
            }
            else
            {
                aliasingId = attributeId;
            }

            aliasingMap->aliasingAttributes.push_back(aliasingId);
            ASSERT(!mIsAliasingAttributeById[aliasingId]);
            mIsAliasingAttributeById[aliasingId] = true;
        }
    }
}

void SpirvVertexAttributeAliasingTransformer::transformInstruction()
{
    uint32_t wordCount;
    spv::Op opCode;
    const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);

    if (opCode == spv::OpFunction)
    {
        // Declare the expanded matrix variables right before the first function declaration.
        if (!mHaveMatricesExpanded)
        {
            declareExpandedMatrixVectors();
            mHaveMatricesExpanded = true;
        }

        // SPIR-V is structured in sections.  Function declarations come last.
        mIsInFunctionSection = true;

        // The matrix attribute declarations have been changed to have Private storage class, and
        // they are initialized from the expanded (and potentially aliased) Input vectors.  This is
        // done at the beginning of the entry point.

        spirv::IdResultType id;
        spirv::IdResult functionId;
        spv::FunctionControlMask functionControl;
        spirv::IdRef functionType;
        spirv::ParseFunction(instruction, &id, &functionId, &functionControl, &functionType);

        mWriteExpandedMatrixInitialization = functionId == mEntryPointId;
    }

    // Only look at interesting instructions.
    TransformationState transformationState = TransformationState::Unchanged;

    if (mIsInFunctionSection)
    {
        // Write expanded matrix initialization right after the entry point's OpFunction and any
        // instruction that must come immediately after it.
        if (mWriteExpandedMatrixInitialization && opCode != spv::OpFunction &&
            opCode != spv::OpFunctionParameter && opCode != spv::OpLabel &&
            opCode != spv::OpVariable)
        {
            writeExpandedMatrixInitialization();
            mWriteExpandedMatrixInitialization = false;
        }

        // Look at in-function opcodes.
        switch (opCode)
        {
            case spv::OpAccessChain:
            case spv::OpInBoundsAccessChain:
                transformationState = transformAccessChain(instruction);
                break;
            case spv::OpLoad:
                transformationState = transformLoad(instruction);
                break;
            default:
                break;
        }
    }
    else
    {
        // Look at global declaration opcodes.
        switch (opCode)
        {
            // Informational instructions:
            case spv::OpTypeFloat:
                visitTypeFloat(instruction);
                break;
            case spv::OpTypeVector:
                visitTypeVector(instruction);
                break;
            case spv::OpTypeMatrix:
                visitTypeMatrix(instruction);
                break;
            case spv::OpTypePointer:
                visitTypePointer(instruction);
                break;
            // Instructions that may need transformation:
            case spv::OpEntryPoint:
                transformationState = transformEntryPoint(instruction);
                break;
            case spv::OpName:
                transformationState = transformName(instruction);
                break;
            case spv::OpDecorate:
                transformationState = transformDecorate(instruction);
                break;
            case spv::OpVariable:
                transformationState = transformVariable(instruction);
                break;
            default:
                break;
        }
    }

    // If the instruction was not transformed, copy it to output as is.
    if (transformationState == TransformationState::Unchanged)
    {
        copyInstruction(instruction, wordCount);
    }

    // Advance to next instruction.
    mCurrentWord += wordCount;
}

spirv::IdRef SpirvVertexAttributeAliasingTransformer::getAliasingAttributeReplacementId(
    spirv::IdRef aliasingId,
    uint32_t offset) const
{
    // Get variable info corresponding to the aliasing attribute.
    const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[aliasingId];
    ValidateShaderInterfaceVariableIsAttribute(aliasingInfo);

    // Find the replacement attribute.
    const AliasingAttributeMap *aliasingMap =
        &mAliasingAttributeMap[aliasingInfo->location + offset];
    ValidateIsAliasingAttribute(aliasingMap, aliasingId);

    const spirv::IdRef replacementId(aliasingMap->attribute);
    ASSERT(replacementId.valid() && replacementId < mIsAliasingAttributeById.size());
    ASSERT(!mIsAliasingAttributeById[replacementId]);

    return replacementId;
}

bool SpirvVertexAttributeAliasingTransformer::isMatrixAttribute(spirv::IdRef id) const
{
    return mExpandedMatrixFirstVectorIdById[id].valid();
}

void SpirvVertexAttributeAliasingTransformer::visitTypeFloat(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::LiteralInteger width;
    spirv::ParseTypeFloat(instruction, &id, &width);

    // Only interested in OpTypeFloat 32.
    if (width == 32)
    {
        ASSERT(!mFloatTypes[1].valid());
        mFloatTypes[1] = id;
    }
}

void SpirvVertexAttributeAliasingTransformer::visitTypeVector(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::IdRef componentId;
    spirv::LiteralInteger componentCount;
    spirv::ParseTypeVector(instruction, &id, &componentId, &componentCount);

    // Only interested in OpTypeVector %f32 N, where %f32 is the id of OpTypeFloat 32.
    if (componentId == mFloatTypes[1])
    {
        ASSERT(componentCount >= 2 && componentCount <= 4);
        ASSERT(!mFloatTypes[componentCount].valid());
        mFloatTypes[componentCount] = id;
    }
}

void SpirvVertexAttributeAliasingTransformer::visitTypeMatrix(const uint32_t *instruction)
{
    spirv::IdResult id;
    spirv::IdRef columnType;
    spirv::LiteralInteger columnCount;
    spirv::ParseTypeMatrix(instruction, &id, &columnType, &columnCount);

    // Only interested in OpTypeMatrix %vecN, where %vecN is the id of OpTypeVector %f32 N.
    // This is only for square matN types (as allowed by GLSL ES 1.00), so columnCount is the same
    // as rowCount.
    if (columnType == mFloatTypes[columnCount])
    {
        ASSERT(!mMatrixTypes[columnCount].valid());
        mMatrixTypes[columnCount] = id;
    }
}

void SpirvVertexAttributeAliasingTransformer::visitTypePointer(const uint32_t *instruction)
{
    spirv::IdResult id;
    spv::StorageClass storageClass;
    spirv::IdRef typeId;
    spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);

    // Only interested in OpTypePointer Input %vecN, where %vecN is the id of OpTypeVector %f32 N,
    // as well as OpTypePointer Private %matN, where %matN is the id of OpTypeMatrix %vecN N.
    // This is only for matN types (as allowed by GLSL ES 1.00), so N >= 2.
    if (storageClass == spv::StorageClassInput)
    {
        for (size_t n = 2; n < mFloatTypes.size(); ++n)
        {
            if (typeId == mFloatTypes[n])
            {
                ASSERT(!mInputTypePointers[n].valid());
                mInputTypePointers[n] = id;
                break;
            }
        }
    }
    else if (storageClass == spv::StorageClassPrivate)
    {
        ASSERT(mFloatTypes.size() == mMatrixTypes.size());
        for (size_t n = 2; n < mMatrixTypes.size(); ++n)
        {
            // Note that Private types may not be unique, as the previous transformation can
            // generate duplicates.
            if (typeId == mFloatTypes[n])
            {
                mPrivateFloatTypePointers[n] = id;
                break;
            }
            if (typeId == mMatrixTypes[n])
            {
                mPrivateMatrixTypePointers[n] = id;
                break;
            }
        }
    }
}

TransformationState SpirvVertexAttributeAliasingTransformer::transformEntryPoint(
    const uint32_t *instruction)
{
    // Should only have one EntryPoint
    ASSERT(!mEntryPointId.valid());

    // Remove aliasing attributes from the shader interface declaration.
    spv::ExecutionModel executionModel;
    spirv::LiteralString name;
    spirv::IdRefList interfaceList;
    spirv::ParseEntryPoint(instruction, &executionModel, &mEntryPointId, &name, &interfaceList);

    // As a first pass, filter out matrix attributes and append their replacement vectors.
    size_t originalInterfaceListSize = interfaceList.size();
    for (size_t index = 0; index < originalInterfaceListSize; ++index)
    {
        const spirv::IdRef matrixId(interfaceList[index]);

        if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
        {
            continue;
        }

        const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
        ValidateShaderInterfaceVariableIsAttribute(info);

        // Replace the matrix id with its first vector id.
        const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);
        interfaceList[index] = vec0Id;

        // Append the rest of the vectors to the entry point.
        for (uint32_t offset = 1; offset < info->attributeLocationCount; ++offset)
        {
            const spirv::IdRef vecId(vec0Id + offset);
            interfaceList.push_back(vecId);
        }
    }

    // Filter out aliasing attributes from entry point interface declaration.
    size_t writeIndex = 0;
    for (size_t index = 0; index < interfaceList.size(); ++index)
    {
        const spirv::IdRef id(interfaceList[index]);

        // If this is an attribute that's aliasing another one in the same location, remove it.
        if (mIsAliasingAttributeById[id])
        {
            const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
            ValidateShaderInterfaceVariableIsAttribute(info);

            // The following assertion is only valid for non-matrix attributes.
            if (info->attributeLocationCount == 1)
            {
                const AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[info->location];
                ValidateIsAliasingAttribute(aliasingMap, id);
            }

            continue;
        }

        interfaceList[writeIndex] = id;
        ++writeIndex;
    }

    // Update the number of interface variables.
    interfaceList.resize(writeIndex);

    // Write the entry point with the aliasing attributes removed.
    spirv::WriteEntryPoint(mSpirvBlobOut, executionModel, mEntryPointId, name, interfaceList);

    return TransformationState::Transformed;
}

TransformationState SpirvVertexAttributeAliasingTransformer::transformName(
    const uint32_t *instruction)
{
    spirv::IdRef id;
    spirv::LiteralString name;
    spirv::ParseName(instruction, &id, &name);

    // If id is not that of an aliasing attribute, there's nothing to do.
    ASSERT(id < mIsAliasingAttributeById.size());
    if (!mIsAliasingAttributeById[id])
    {
        return TransformationState::Unchanged;
    }

    // Drop debug annotations for this id.
    return TransformationState::Transformed;
}

TransformationState SpirvVertexAttributeAliasingTransformer::transformDecorate(
    const uint32_t *instruction)
{
    spirv::IdRef id;
    spv::Decoration decoration;
    spirv::ParseDecorate(instruction, &id, &decoration, nullptr);

    if (isMatrixAttribute(id))
    {
        // If it's a matrix attribute, it's expanded to multiple vectors.  Insert the Location
        // decorations for these vectors here.

        // Keep all decorations except for Location.
        if (decoration != spv::DecorationLocation)
        {
            return TransformationState::Unchanged;
        }

        const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
        ValidateShaderInterfaceVariableIsAttribute(info);

        const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[id]);
        ASSERT(vec0Id.valid());

        for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
        {
            const spirv::IdRef vecId(vec0Id + offset);
            if (mIsAliasingAttributeById[vecId])
            {
                continue;
            }

            spirv::WriteDecorate(mSpirvBlobOut, vecId, decoration,
                                 {spirv::LiteralInteger(info->location + offset)});
        }
    }
    else
    {
        // If id is not that of an active attribute, there's nothing to do.
        const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
        if (info == nullptr || info->attributeComponentCount == 0 ||
            !info->activeStages[gl::ShaderType::Vertex])
        {
            return TransformationState::Unchanged;
        }

        // Always drop RelaxedPrecision from input attributes.  The temporary variable the attribute
        // is loaded into has RelaxedPrecision and will implicitly convert.
        if (decoration == spv::DecorationRelaxedPrecision)
        {
            return TransformationState::Transformed;
        }

        // If id is not that of an aliasing attribute, there's nothing else to do.
        ASSERT(id < mIsAliasingAttributeById.size());
        if (!mIsAliasingAttributeById[id])
        {
            return TransformationState::Unchanged;
        }
    }

    // Drop every decoration for this id.
    return TransformationState::Transformed;
}

TransformationState SpirvVertexAttributeAliasingTransformer::transformVariable(
    const uint32_t *instruction)
{
    spirv::IdResultType typeId;
    spirv::IdResult id;
    spv::StorageClass storageClass;
    spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);

    if (!isMatrixAttribute(id))
    {
        // If id is not that of an aliasing attribute, there's nothing to do.  Note that matrix
        // declarations are always replaced.
        ASSERT(id < mIsAliasingAttributeById.size());
        if (!mIsAliasingAttributeById[id])
        {
            return TransformationState::Unchanged;
        }
    }

    ASSERT(storageClass == spv::StorageClassInput);

    // Drop the declaration.
    return TransformationState::Transformed;
}

TransformationState SpirvVertexAttributeAliasingTransformer::transformAccessChain(
    const uint32_t *instruction)
{
    spirv::IdResultType typeId;
    spirv::IdResult id;
    spirv::IdRef baseId;
    spirv::IdRefList indexList;
    spirv::ParseAccessChain(instruction, &typeId, &id, &baseId, &indexList);

    if (isMatrixAttribute(baseId))
    {
        // Write a modified OpAccessChain instruction.  Only modification is that the %type is
        // replaced with the Private version of it.  If there is one %index, that would be a vector
        // type, and if there are two %index'es, it's a float type.
        spirv::IdRef replacementTypeId;

        if (indexList.size() == 1)
        {
            // If indexed once, it uses a vector type.
            const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
            ValidateShaderInterfaceVariableIsAttribute(info);

            const uint32_t componentCount = info->attributeComponentCount;

            // %type must have been the Input vector type with the matrice's component size.
            ASSERT(typeId == mInputTypePointers[componentCount]);

            // Replace the type with the corresponding Private one.
            replacementTypeId = mPrivateFloatTypePointers[componentCount];
        }
        else
        {
            // If indexed twice, it uses the float type.
            ASSERT(indexList.size() == 2);

            // Replace the type with the Private pointer to float32.
            replacementTypeId = mPrivateFloatTypePointers[1];
        }

        spirv::WriteAccessChain(mSpirvBlobOut, replacementTypeId, id, baseId, indexList);
    }
    else
    {
        // If base id is not that of an aliasing attribute, there's nothing to do.
        ASSERT(baseId < mIsAliasingAttributeById.size());
        if (!mIsAliasingAttributeById[baseId])
        {
            return TransformationState::Unchanged;
        }

        // Find the replacement attribute for the aliasing one.
        const spirv::IdRef replacementId(getAliasingAttributeReplacementId(baseId, 0));

        // Get variable info corresponding to the replacement attribute.
        const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
        ValidateShaderInterfaceVariableIsAttribute(replacementInfo);

        // Write a modified OpAccessChain instruction.  Currently, the instruction is:
        //
        //     %id = OpAccessChain %type %base %index
        //
        // This is modified to:
        //
        //     %id = OpAccessChain %type %replacement %index
        //
        // Note that the replacement has at least as many components as the aliasing attribute,
        // and both attributes start at component 0 (GLSL ES restriction).  So, indexing the
        // replacement attribute with the same index yields the same result and type.
        spirv::WriteAccessChain(mSpirvBlobOut, typeId, id, replacementId, indexList);
    }

    return TransformationState::Transformed;
}

void SpirvVertexAttributeAliasingTransformer::transformLoadHelper(spirv::IdRef pointerId,
                                                                  spirv::IdRef typeId,
                                                                  spirv::IdRef replacementId,
                                                                  spirv::IdRef resultId)
{
    // Get variable info corresponding to the replacement attribute.
    const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
    ValidateShaderInterfaceVariableIsAttribute(replacementInfo);

    // Currently, the instruction is:
    //
    //     %id = OpLoad %type %pointer
    //
    // This is modified to:
    //
    //     %newId = OpLoad %replacementType %replacement
    //
    const spirv::IdRef loadResultId(getNewId());
    const spirv::IdRef replacementTypeId(mFloatTypes[replacementInfo->attributeComponentCount]);
    ASSERT(replacementTypeId.valid());

    spirv::WriteLoad(mSpirvBlobOut, replacementTypeId, loadResultId, replacementId, nullptr);

    // If swizzle is not necessary, assign %newId to %resultId.
    const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[pointerId];
    if (aliasingInfo->attributeComponentCount == replacementInfo->attributeComponentCount)
    {
        spirv::WriteCopyObject(mSpirvBlobOut, typeId, resultId, loadResultId);
        return;
    }

    // Take as many components from the replacement as the aliasing attribute wanted.  This is done
    // by either of the following instructions:
    //
    // - If aliasing attribute has only one component:
    //
    //     %resultId = OpCompositeExtract %floatType %newId 0
    //
    // - If aliasing attribute has more than one component:
    //
    //     %resultId = OpVectorShuffle %vecType %newId %newId 0 1 ...
    //
    ASSERT(aliasingInfo->attributeComponentCount < replacementInfo->attributeComponentCount);
    ASSERT(mFloatTypes[aliasingInfo->attributeComponentCount] == typeId);

    if (aliasingInfo->attributeComponentCount == 1)
    {
        spirv::WriteCompositeExtract(mSpirvBlobOut, typeId, resultId, loadResultId,
                                     {spirv::LiteralInteger(0)});
    }
    else
    {
        spirv::LiteralIntegerList swizzle = {spirv::LiteralInteger(0), spirv::LiteralInteger(1),
                                             spirv::LiteralInteger(2), spirv::LiteralInteger(3)};
        swizzle.resize(aliasingInfo->attributeComponentCount);

        spirv::WriteVectorShuffle(mSpirvBlobOut, typeId, resultId, loadResultId, loadResultId,
                                  swizzle);
    }
}

TransformationState SpirvVertexAttributeAliasingTransformer::transformLoad(
    const uint32_t *instruction)
{
    spirv::IdResultType typeId;
    spirv::IdResult id;
    spirv::IdRef pointerId;
    ParseLoad(instruction, &typeId, &id, &pointerId, nullptr);

    // Currently, the instruction is:
    //
    //     %id = OpLoad %type %pointer
    //
    // If non-matrix, this is modifed to load from the aliasing vector instead if aliasing.
    //
    // If matrix, this is modified such that %type points to the Private version of it.
    //
    if (isMatrixAttribute(pointerId))
    {
        const ShaderInterfaceVariableInfo *info = mVariableInfoById[pointerId];
        ValidateShaderInterfaceVariableIsAttribute(info);

        const spirv::IdRef replacementTypeId(mMatrixTypes[info->attributeLocationCount]);

        spirv::WriteLoad(mSpirvBlobOut, replacementTypeId, id, pointerId, nullptr);
    }
    else
    {
        // If pointer id is not that of an aliasing attribute, there's nothing to do.
        ASSERT(pointerId < mIsAliasingAttributeById.size());
        if (!mIsAliasingAttributeById[pointerId])
        {
            return TransformationState::Unchanged;
        }

        // Find the replacement attribute for the aliasing one.
        const spirv::IdRef replacementId(getAliasingAttributeReplacementId(pointerId, 0));

        // Replace the load instruction by a load from the replacement id.
        transformLoadHelper(pointerId, typeId, replacementId, id);
    }

    return TransformationState::Transformed;
}

void SpirvVertexAttributeAliasingTransformer::declareExpandedMatrixVectors()
{
    // Go through matrix attributes and expand them.
    for (uint32_t matrixIdIndex = spirv::kMinValidId;
         matrixIdIndex < mExpandedMatrixFirstVectorIdById.size(); ++matrixIdIndex)
    {
        const spirv::IdRef matrixId(matrixIdIndex);

        if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
        {
            continue;
        }

        const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);

        const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
        ValidateShaderInterfaceVariableIsAttribute(info);

        // Need to generate the following:
        //
        //     %privateType = OpTypePointer Private %matrixType
        //     %id = OpVariable %privateType Private
        //     %vecType = OpTypePointer %vecType Input
        //     %vec0 = OpVariable %vecType Input
        //     ...
        //     %vecN-1 = OpVariable %vecType Input
        const uint32_t componentCount = info->attributeComponentCount;
        const uint32_t locationCount  = info->attributeLocationCount;
        ASSERT(componentCount == locationCount);
        ASSERT(mMatrixTypes[locationCount].valid());

        // OpTypePointer Private %matrixType
        spirv::IdRef privateType(mPrivateMatrixTypePointers[locationCount]);
        if (!privateType.valid())
        {
            privateType                               = getNewId();
            mPrivateMatrixTypePointers[locationCount] = privateType;
            spirv::WriteTypePointer(mSpirvBlobOut, privateType, spv::StorageClassPrivate,
                                    mMatrixTypes[locationCount]);
        }

        // OpVariable %privateType Private
        spirv::WriteVariable(mSpirvBlobOut, privateType, matrixId, spv::StorageClassPrivate,
                             nullptr);

        // If the OpTypePointer is not declared for the vector type corresponding to each location,
        // declare it now.
        //
        //     %vecType = OpTypePointer %vecType Input
        spirv::IdRef inputType(mInputTypePointers[componentCount]);
        if (!inputType.valid())
        {
            inputType                          = getNewId();
            mInputTypePointers[componentCount] = inputType;
            spirv::WriteTypePointer(mSpirvBlobOut, inputType, spv::StorageClassInput,
                                    mFloatTypes[componentCount]);
        }

        // Declare a vector for each column of the matrix.
        for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
        {
            const spirv::IdRef vecId(vec0Id + offset);
            if (!mIsAliasingAttributeById[vecId])
            {
                spirv::WriteVariable(mSpirvBlobOut, inputType, vecId, spv::StorageClassInput,
                                     nullptr);
            }
        }
    }

    // Additionally, declare OpTypePointer Private %mFloatTypes[i] in case needed (used in
    // Op*AccessChain instructions, if any).
    for (size_t n = 1; n < mFloatTypes.size(); ++n)
    {
        if (mFloatTypes[n].valid() && !mPrivateFloatTypePointers[n].valid())
        {
            const spirv::IdRef privateType(getNewId());
            mPrivateFloatTypePointers[n] = privateType;
            spirv::WriteTypePointer(mSpirvBlobOut, privateType, spv::StorageClassPrivate,
                                    mFloatTypes[n]);
        }
    }
}

void SpirvVertexAttributeAliasingTransformer::writeExpandedMatrixInitialization()
{
    // Go through matrix attributes and initialize them.  Note that their declaration is replaced
    // with a Private storage class, but otherwise has the same id.
    for (uint32_t matrixIdIndex = spirv::kMinValidId;
         matrixIdIndex < mExpandedMatrixFirstVectorIdById.size(); ++matrixIdIndex)
    {
        const spirv::IdRef matrixId(matrixIdIndex);

        if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
        {
            continue;
        }

        const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);

        // For every matrix, need to generate the following:
        //
        //     %vec0Id = OpLoad %vecType %vec0Pointer
        //     ...
        //     %vecN-1Id = OpLoad %vecType %vecN-1Pointer
        //     %mat = OpCompositeConstruct %matrixType %vec0 ... %vecN-1
        //     OpStore %matrixId %mat

        const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
        ValidateShaderInterfaceVariableIsAttribute(info);

        spirv::IdRefList vecLoadIds;
        const uint32_t locationCount = info->attributeLocationCount;
        for (uint32_t offset = 0; offset < locationCount; ++offset)
        {
            const spirv::IdRef vecId(vec0Id + offset);

            // Load into temporary, potentially through an aliasing vector.
            spirv::IdRef replacementId(vecId);
            ASSERT(vecId < mIsAliasingAttributeById.size());
            if (mIsAliasingAttributeById[vecId])
            {
                replacementId = getAliasingAttributeReplacementId(vecId, offset);
            }

            // Write a load instruction from the replacement id.
            vecLoadIds.push_back(getNewId());
            transformLoadHelper(matrixId, mFloatTypes[info->attributeComponentCount], replacementId,
                                vecLoadIds.back());
        }

        // Aggregate the vector loads into a matrix.
        ASSERT(mMatrixTypes[locationCount].valid());
        const spirv::IdRef compositeId(getNewId());
        spirv::WriteCompositeConstruct(mSpirvBlobOut, mMatrixTypes[locationCount], compositeId,
                                       vecLoadIds);

        // Store it in the private variable.
        spirv::WriteStore(mSpirvBlobOut, matrixId, compositeId, nullptr);
    }
}

bool HasAliasingAttributes(const ShaderInterfaceVariableInfoMap &variableInfoMap)
{
    gl::AttributesMask isLocationAssigned;

    for (const auto &infoIter : variableInfoMap.getIterator(gl::ShaderType::Vertex))
    {
        const ShaderInterfaceVariableInfo &info = infoIter.second;

        // Ignore non attribute ids.
        if (info.attributeComponentCount == 0)
        {
            continue;
        }

        ASSERT(info.activeStages[gl::ShaderType::Vertex]);
        ASSERT(info.location != ShaderInterfaceVariableInfo::kInvalid);
        ASSERT(info.attributeLocationCount > 0);

        for (uint8_t offset = 0; offset < info.attributeLocationCount; ++offset)
        {
            uint32_t location = info.location + offset;

            // If there's aliasing, return immediately.
            if (isLocationAssigned.test(location))
            {
                return true;
            }

            isLocationAssigned.set(location);
        }
    }

    return false;
}
}  // anonymous namespace

// ShaderInterfaceVariableInfo implementation.
const uint32_t ShaderInterfaceVariableInfo::kInvalid;

ShaderInterfaceVariableInfo::ShaderInterfaceVariableInfo() {}

// ShaderInterfaceVariableInfoMap implementation.
ShaderInterfaceVariableInfoMap::ShaderInterfaceVariableInfoMap() = default;

ShaderInterfaceVariableInfoMap::~ShaderInterfaceVariableInfoMap() = default;

void ShaderInterfaceVariableInfoMap::clear()
{
    for (VariableNameToInfoMap &shaderMap : mData)
    {
        shaderMap.clear();
    }
}

bool ShaderInterfaceVariableInfoMap::contains(gl::ShaderType shaderType,
                                              const std::string &variableName) const
{
    return mData[shaderType].find(variableName) != mData[shaderType].end();
}

const ShaderInterfaceVariableInfo &ShaderInterfaceVariableInfoMap::get(
    gl::ShaderType shaderType,
    const std::string &variableName) const
{
    auto it = mData[shaderType].find(variableName);
    ASSERT(it != mData[shaderType].end());
    return it->second;
}

ShaderInterfaceVariableInfo &ShaderInterfaceVariableInfoMap::get(gl::ShaderType shaderType,
                                                                 const std::string &variableName)
{
    auto it = mData[shaderType].find(variableName);
    ASSERT(it != mData[shaderType].end());
    return it->second;
}

ShaderInterfaceVariableInfo &ShaderInterfaceVariableInfoMap::add(gl::ShaderType shaderType,
                                                                 const std::string &variableName)
{
    ASSERT(!contains(shaderType, variableName));
    return mData[shaderType][variableName];
}

ShaderInterfaceVariableInfo &ShaderInterfaceVariableInfoMap::addOrGet(
    gl::ShaderType shaderType,
    const std::string &variableName)
{
    return mData[shaderType][variableName];
}

ShaderInterfaceVariableInfoMap::Iterator ShaderInterfaceVariableInfoMap::getIterator(
    gl::ShaderType shaderType) const
{
    return Iterator(mData[shaderType].begin(), mData[shaderType].end());
}

void GlslangInitialize()
{
    int result = ShInitialize();
    ASSERT(result != 0);
    GlslangWarmup();
}

void GlslangRelease()
{
    int result = ShFinalize();
    ASSERT(result != 0);
}

// Strip indices from the name.  If there are non-zero indices, return false to indicate that this
// image uniform doesn't require set/binding.  That is done on index 0.
bool GetImageNameWithoutIndices(std::string *name)
{
    if (name->back() != ']')
    {
        return true;
    }

    if (!UniformNameIsIndexZero(*name))
    {
        return false;
    }

    // Strip all indices
    *name = name->substr(0, name->find('['));
    return true;
}

std::string GlslangGetMappedSamplerName(const std::string &originalName)
{
    std::string samplerName = originalName;

    // Samplers in structs are extracted.
    std::replace(samplerName.begin(), samplerName.end(), '.', '_');

    // Remove array elements
    auto out = samplerName.begin();
    for (auto in = samplerName.begin(); in != samplerName.end(); in++)
    {
        if (*in == '[')
        {
            while (*in != ']')
            {
                in++;
                ASSERT(in != samplerName.end());
            }
        }
        else
        {
            *out++ = *in;
        }
    }

    samplerName.erase(out, samplerName.end());

    if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
    {
        samplerName = sh::kUserDefinedNamePrefix + samplerName;
    }

    return samplerName;
}

std::string GetXfbBufferName(const uint32_t bufferIndex)
{
    return sh::vk::kXfbEmulationBufferBlockName + Str(bufferIndex);
}

void GlslangAssignLocations(const GlslangSourceOptions &options,
                            const gl::ProgramState &programState,
                            const gl::ProgramVaryingPacking &varyingPacking,
                            const gl::ShaderType shaderType,
                            const gl::ShaderType frontShaderType,
                            bool isTransformFeedbackStage,
                            GlslangProgramInterfaceInfo *programInterfaceInfo,
                            ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    const gl::ProgramExecutable &programExecutable = programState.getExecutable();

    // Assign outputs to the fragment shader, if any.
    if ((shaderType == gl::ShaderType::Fragment) &&
        programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment))
    {
        AssignOutputLocations(programState, gl::ShaderType::Fragment, variableInfoMapOut);
    }

    // Assign attributes to the vertex shader, if any.
    if ((shaderType == gl::ShaderType::Vertex) &&
        programExecutable.hasLinkedShaderStage(gl::ShaderType::Vertex))
    {
        AssignAttributeLocations(programExecutable, gl::ShaderType::Vertex, variableInfoMapOut);
    }

    if (!programExecutable.hasLinkedShaderStage(gl::ShaderType::Compute))
    {
        const gl::VaryingPacking &inputPacking  = varyingPacking.getInputPacking(shaderType);
        const gl::VaryingPacking &outputPacking = varyingPacking.getOutputPacking(shaderType);

        // Assign location to varyings generated for transform feedback capture
        if (options.supportsTransformFeedbackExtension &&
            gl::ShaderTypeSupportsTransformFeedback(shaderType))
        {
            AssignTransformFeedbackExtensionLocations(shaderType, programState,
                                                      isTransformFeedbackStage,
                                                      programInterfaceInfo, variableInfoMapOut);
        }

        // Assign varying locations.
        if (shaderType != gl::ShaderType::Vertex)
        {
            AssignVaryingLocations(options, inputPacking, shaderType, frontShaderType,
                                   programInterfaceInfo, variableInfoMapOut);
        }
        if (shaderType != gl::ShaderType::Fragment)
        {
            AssignVaryingLocations(options, outputPacking, shaderType, frontShaderType,
                                   programInterfaceInfo, variableInfoMapOut);
        }

        // Assign qualifiers to all varyings captured by transform feedback
        if (!programExecutable.getLinkedTransformFeedbackVaryings().empty() &&
            shaderType == programExecutable.getLinkedTransformFeedbackStage())
        {
            AssignTransformFeedbackQualifiers(programExecutable, outputPacking, shaderType,
                                              options.supportsTransformFeedbackExtension,
                                              variableInfoMapOut);
        }
    }

    AssignUniformBindings(options, programExecutable, shaderType, programInterfaceInfo,
                          variableInfoMapOut);
    AssignTextureBindings(options, programExecutable, shaderType, programInterfaceInfo,
                          variableInfoMapOut);
    AssignNonTextureBindings(options, programExecutable, shaderType, programInterfaceInfo,
                             variableInfoMapOut);

    if (options.supportsTransformFeedbackEmulation &&
        gl::ShaderTypeSupportsTransformFeedback(shaderType))
    {
        // If transform feedback emulation is not enabled, mark all transform feedback output
        // buffers as inactive.
        isTransformFeedbackStage =
            isTransformFeedbackStage && options.enableTransformFeedbackEmulation;

        AssignTransformFeedbackEmulationBindings(shaderType, programState, isTransformFeedbackStage,
                                                 programInterfaceInfo, variableInfoMapOut);
    }
}

void GlslangGetShaderSource(const GlslangSourceOptions &options,
                            const gl::ProgramState &programState,
                            const gl::ProgramLinkedResources &resources,
                            GlslangProgramInterfaceInfo *programInterfaceInfo,
                            gl::ShaderMap<std::string> *shaderSourcesOut,
                            ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
    for (const gl::ShaderType shaderType : gl::AllShaderTypes())
    {
        gl::Shader *glShader            = programState.getAttachedShader(shaderType);
        (*shaderSourcesOut)[shaderType] = glShader ? glShader->getTranslatedSource() : "";
    }

    gl::ShaderType xfbStage        = programState.getAttachedTransformFeedbackStage();
    gl::ShaderType frontShaderType = gl::ShaderType::InvalidEnum;

    for (const gl::ShaderType shaderType : programState.getExecutable().getLinkedShaderStages())
    {
        const bool isXfbStage =
            shaderType == xfbStage && !programState.getLinkedTransformFeedbackVaryings().empty();
        GlslangAssignLocations(options, programState, resources.varyingPacking, shaderType,
                               frontShaderType, isXfbStage, programInterfaceInfo,
                               variableInfoMapOut);

        frontShaderType = shaderType;
    }
}

angle::Result GlslangTransformSpirvCode(const GlslangErrorCallback &callback,
                                        const GlslangSpirvOptions &options,
                                        const ShaderInterfaceVariableInfoMap &variableInfoMap,
                                        const spirv::Blob &initialSpirvBlob,
                                        spirv::Blob *spirvBlobOut)
{
    if (initialSpirvBlob.empty())
    {
        return angle::Result::Continue;
    }

#if defined(ANGLE_DEBUG_SPIRV_TRANSFORMER) && ANGLE_DEBUG_SPIRV_TRANSFORMER
    spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1);
    spirvTools.SetMessageConsumer(ValidateSpirvMessage);
    std::string readableSpirv;
    spirvTools.Disassemble(initialSpirvBlob, &readableSpirv, 0);
    fprintf(stderr, "%s\n", readableSpirv.c_str());
#endif  // defined(ANGLE_DEBUG_SPIRV_TRANSFORMER) && ANGLE_DEBUG_SPIRV_TRANSFORMER

    // Transform the SPIR-V code by assigning location/set/binding values.
    SpirvTransformer transformer(initialSpirvBlob, options, variableInfoMap, spirvBlobOut);
    ANGLE_GLSLANG_CHECK(callback, transformer.transform(), GlslangError::InvalidSpirv);

    // If there are aliasing vertex attributes, transform the SPIR-V again to remove them.
    if (options.shaderType == gl::ShaderType::Vertex && HasAliasingAttributes(variableInfoMap))
    {
        spirv::Blob preTransformBlob = std::move(*spirvBlobOut);
        SpirvVertexAttributeAliasingTransformer aliasingTransformer(
            preTransformBlob, variableInfoMap, std::move(transformer.getVariableInfoByIdMap()),
            spirvBlobOut);
        ANGLE_GLSLANG_CHECK(callback, aliasingTransformer.transform(), GlslangError::InvalidSpirv);
    }

    ASSERT(spirv::Validate(*spirvBlobOut));

    return angle::Result::Continue;
}

angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
                                        const gl::ShaderBitSet &linkedShaderStages,
                                        const gl::Caps &glCaps,
                                        const gl::ShaderMap<std::string> &shaderSources,
                                        gl::ShaderMap<spirv::Blob> *spirvBlobsOut)
{
    TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
    GetBuiltInResourcesFromCaps(glCaps, &builtInResources);

    glslang::TShader vertexShader(EShLangVertex);
    glslang::TShader fragmentShader(EShLangFragment);
    glslang::TShader geometryShader(EShLangGeometry);
    glslang::TShader tessControlShader(EShLangTessControl);
    glslang::TShader tessEvaluationShader(EShLangTessEvaluation);
    glslang::TShader computeShader(EShLangCompute);

    gl::ShaderMap<glslang::TShader *> shaders = {
        {gl::ShaderType::Vertex, &vertexShader},
        {gl::ShaderType::Fragment, &fragmentShader},
        {gl::ShaderType::TessControl, &tessControlShader},
        {gl::ShaderType::TessEvaluation, &tessEvaluationShader},
        {gl::ShaderType::Geometry, &geometryShader},
        {gl::ShaderType::Compute, &computeShader},
    };
    for (const gl::ShaderType shaderType : linkedShaderStages)
    {
        if (shaderSources[shaderType].empty())
        {
            continue;
        }

        glslang::TProgram program;

        ANGLE_TRY(CompileShader(callback, builtInResources, shaderType, shaderSources[shaderType],
                                shaders[shaderType], &program));

        ANGLE_TRY(LinkProgram(callback, &program));

        glslang::TIntermediate *intermediate = program.getIntermediate(kShLanguageMap[shaderType]);
        glslang::GlslangToSpv(*intermediate, (*spirvBlobsOut)[shaderType]);
    }

    return angle::Result::Continue;
}

}  // namespace rx
