Rename VariableInfo files to CollectVariables

Files that only have a single function should be named consistently
with the function. Unnecessary include in CallDAG.h is removed.

BUG=angleproject:2068
TEST=angle_unittests

Change-Id: I27d55a766b9eb66fcfd1e0a2341a2843bb9dc5bb
Reviewed-on: https://chromium-review.googlesource.com/608368
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
new file mode 100644
index 0000000..4ffae59
--- /dev/null
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -0,0 +1,702 @@
+//
+// Copyright (c) 2002-2013 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.
+//
+// CollectVariables.cpp: Collect lists of shader interface variables based on the AST.
+
+#include "compiler/translator/CollectVariables.h"
+
+#include "angle_gl.h"
+#include "common/utilities.h"
+#include "compiler/translator/HashNames.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
+{
+    switch (blockStorage)
+    {
+        case EbsPacked:
+            return BLOCKLAYOUT_PACKED;
+        case EbsShared:
+            return BLOCKLAYOUT_SHARED;
+        case EbsStd140:
+            return BLOCKLAYOUT_STANDARD;
+        default:
+            UNREACHABLE();
+            return BLOCKLAYOUT_SHARED;
+    }
+}
+
+BlockType GetBlockType(TQualifier qualifier)
+{
+    switch (qualifier)
+    {
+        case EvqUniform:
+            return BlockType::BLOCK_UNIFORM;
+        case EvqBuffer:
+            return BlockType::BLOCK_BUFFER;
+        default:
+            UNREACHABLE();
+            return BlockType::BLOCK_UNIFORM;
+    }
+}
+
+template <class VarT>
+VarT *FindVariable(const TString &name, std::vector<VarT> *infoList)
+{
+    // TODO(zmo): optimize this function.
+    for (size_t ii = 0; ii < infoList->size(); ++ii)
+    {
+        if ((*infoList)[ii].name.c_str() == name)
+            return &((*infoList)[ii]);
+    }
+
+    return nullptr;
+}
+
+// Note that this shouldn't be called for interface blocks - static use information is collected for
+// individual fields in case of interface blocks.
+void MarkStaticallyUsed(ShaderVariable *variable)
+{
+    if (!variable->staticUse)
+    {
+        if (variable->isStruct())
+        {
+            // Conservatively assume all fields are statically used as well.
+            for (auto &field : variable->fields)
+            {
+                MarkStaticallyUsed(&field);
+            }
+        }
+        variable->staticUse = true;
+    }
+}
+
+// Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs,
+// and interface blocks.
+class CollectVariablesTraverser : public TIntermTraverser
+{
+  public:
+    CollectVariablesTraverser(std::vector<Attribute> *attribs,
+                              std::vector<OutputVariable> *outputVariables,
+                              std::vector<Uniform> *uniforms,
+                              std::vector<Varying> *inputVaryings,
+                              std::vector<Varying> *outputVaryings,
+                              std::vector<InterfaceBlock> *uniformBlocks,
+                              std::vector<InterfaceBlock> *shaderStorageBlocks,
+                              ShHashFunction64 hashFunction,
+                              TSymbolTable *symbolTable,
+                              int shaderVersion,
+                              const TExtensionBehavior &extensionBehavior);
+
+    void visitSymbol(TIntermSymbol *symbol) override;
+    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+    bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
+
+  private:
+    void setCommonVariableProperties(const TType &type,
+                                     const TString &name,
+                                     ShaderVariable *variableOut) const;
+
+    Attribute recordAttribute(const TIntermSymbol &variable) const;
+    OutputVariable recordOutputVariable(const TIntermSymbol &variable) const;
+    Varying recordVarying(const TIntermSymbol &variable) const;
+    InterfaceBlock recordInterfaceBlock(const TIntermSymbol &variable) const;
+    Uniform recordUniform(const TIntermSymbol &variable) const;
+
+    void setBuiltInInfoFromSymbolTable(const char *name, ShaderVariable *info);
+
+    void recordBuiltInVaryingUsed(const char *name,
+                                  bool *addedFlag,
+                                  std::vector<Varying> *varyings);
+    void recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag);
+    void recordBuiltInAttributeUsed(const char *name, bool *addedFlag);
+
+    std::vector<Attribute> *mAttribs;
+    std::vector<OutputVariable> *mOutputVariables;
+    std::vector<Uniform> *mUniforms;
+    std::vector<Varying> *mInputVaryings;
+    std::vector<Varying> *mOutputVaryings;
+    std::vector<InterfaceBlock> *mUniformBlocks;
+    std::vector<InterfaceBlock> *mShaderStorageBlocks;
+
+    std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
+
+    bool mDepthRangeAdded;
+    bool mPointCoordAdded;
+    bool mFrontFacingAdded;
+    bool mFragCoordAdded;
+
+    bool mInstanceIDAdded;
+    bool mVertexIDAdded;
+    bool mPositionAdded;
+    bool mPointSizeAdded;
+    bool mLastFragDataAdded;
+    bool mFragColorAdded;
+    bool mFragDataAdded;
+    bool mFragDepthEXTAdded;
+    bool mFragDepthAdded;
+    bool mSecondaryFragColorEXTAdded;
+    bool mSecondaryFragDataEXTAdded;
+
+    ShHashFunction64 mHashFunction;
+
+    int mShaderVersion;
+    const TExtensionBehavior &mExtensionBehavior;
+};
+
+CollectVariablesTraverser::CollectVariablesTraverser(
+    std::vector<sh::Attribute> *attribs,
+    std::vector<sh::OutputVariable> *outputVariables,
+    std::vector<sh::Uniform> *uniforms,
+    std::vector<sh::Varying> *inputVaryings,
+    std::vector<sh::Varying> *outputVaryings,
+    std::vector<sh::InterfaceBlock> *uniformBlocks,
+    std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
+    ShHashFunction64 hashFunction,
+    TSymbolTable *symbolTable,
+    int shaderVersion,
+    const TExtensionBehavior &extensionBehavior)
+    : TIntermTraverser(true, false, false, symbolTable),
+      mAttribs(attribs),
+      mOutputVariables(outputVariables),
+      mUniforms(uniforms),
+      mInputVaryings(inputVaryings),
+      mOutputVaryings(outputVaryings),
+      mUniformBlocks(uniformBlocks),
+      mShaderStorageBlocks(shaderStorageBlocks),
+      mDepthRangeAdded(false),
+      mPointCoordAdded(false),
+      mFrontFacingAdded(false),
+      mFragCoordAdded(false),
+      mInstanceIDAdded(false),
+      mVertexIDAdded(false),
+      mPositionAdded(false),
+      mPointSizeAdded(false),
+      mLastFragDataAdded(false),
+      mFragColorAdded(false),
+      mFragDataAdded(false),
+      mFragDepthEXTAdded(false),
+      mFragDepthAdded(false),
+      mSecondaryFragColorEXTAdded(false),
+      mSecondaryFragDataEXTAdded(false),
+      mHashFunction(hashFunction),
+      mShaderVersion(shaderVersion),
+      mExtensionBehavior(extensionBehavior)
+{
+}
+
+void CollectVariablesTraverser::setBuiltInInfoFromSymbolTable(const char *name,
+                                                              ShaderVariable *info)
+{
+    TVariable *symbolTableVar =
+        reinterpret_cast<TVariable *>(mSymbolTable->findBuiltIn(name, mShaderVersion));
+    ASSERT(symbolTableVar);
+    const TType &type = symbolTableVar->getType();
+
+    info->name       = name;
+    info->mappedName = name;
+    info->type       = GLVariableType(type);
+    info->arraySize  = type.isArray() ? type.getArraySize() : 0;
+    info->precision  = GLVariablePrecision(type);
+}
+
+void CollectVariablesTraverser::recordBuiltInVaryingUsed(const char *name,
+                                                         bool *addedFlag,
+                                                         std::vector<Varying> *varyings)
+{
+    ASSERT(varyings);
+    if (!(*addedFlag))
+    {
+        Varying info;
+        setBuiltInInfoFromSymbolTable(name, &info);
+        info.staticUse   = true;
+        info.isInvariant = mSymbolTable->isVaryingInvariant(name);
+        varyings->push_back(info);
+        (*addedFlag) = true;
+    }
+}
+
+void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag)
+{
+    if (!(*addedFlag))
+    {
+        OutputVariable info;
+        setBuiltInInfoFromSymbolTable(name, &info);
+        info.staticUse = true;
+        mOutputVariables->push_back(info);
+        (*addedFlag) = true;
+    }
+}
+
+void CollectVariablesTraverser::recordBuiltInAttributeUsed(const char *name, bool *addedFlag)
+{
+    if (!(*addedFlag))
+    {
+        Attribute info;
+        setBuiltInInfoFromSymbolTable(name, &info);
+        info.staticUse = true;
+        info.location  = -1;
+        mAttribs->push_back(info);
+        (*addedFlag) = true;
+    }
+}
+
+// We want to check whether a uniform/varying is statically used
+// because we only count the used ones in packing computing.
+// Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
+// toward varying counting if they are statically used in a fragment
+// shader.
+void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
+{
+    ASSERT(symbol != nullptr);
+
+    if (symbol->getName().isInternal())
+    {
+        // Internal variables are not collected.
+        return;
+    }
+
+    ShaderVariable *var       = nullptr;
+    const TString &symbolName = symbol->getName().getString();
+
+    if (IsVaryingIn(symbol->getQualifier()))
+    {
+        var = FindVariable(symbolName, mInputVaryings);
+    }
+    else if (IsVaryingOut(symbol->getQualifier()))
+    {
+        var = FindVariable(symbolName, mOutputVaryings);
+    }
+    else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
+    {
+        UNREACHABLE();
+    }
+    else if (symbolName == "gl_DepthRange")
+    {
+        ASSERT(symbol->getQualifier() == EvqUniform);
+
+        if (!mDepthRangeAdded)
+        {
+            Uniform info;
+            const char kName[] = "gl_DepthRange";
+            info.name          = kName;
+            info.mappedName    = kName;
+            info.type          = GL_STRUCT_ANGLEX;
+            info.arraySize     = 0;
+            info.precision     = GL_NONE;
+            info.staticUse     = true;
+
+            ShaderVariable nearInfo;
+            const char kNearName[] = "near";
+            nearInfo.name          = kNearName;
+            nearInfo.mappedName    = kNearName;
+            nearInfo.type          = GL_FLOAT;
+            nearInfo.arraySize     = 0;
+            nearInfo.precision     = GL_HIGH_FLOAT;
+            nearInfo.staticUse     = true;
+
+            ShaderVariable farInfo;
+            const char kFarName[] = "far";
+            farInfo.name          = kFarName;
+            farInfo.mappedName    = kFarName;
+            farInfo.type          = GL_FLOAT;
+            farInfo.arraySize     = 0;
+            farInfo.precision     = GL_HIGH_FLOAT;
+            farInfo.staticUse     = true;
+
+            ShaderVariable diffInfo;
+            const char kDiffName[] = "diff";
+            diffInfo.name          = kDiffName;
+            diffInfo.mappedName    = kDiffName;
+            diffInfo.type          = GL_FLOAT;
+            diffInfo.arraySize     = 0;
+            diffInfo.precision     = GL_HIGH_FLOAT;
+            diffInfo.staticUse     = true;
+
+            info.fields.push_back(nearInfo);
+            info.fields.push_back(farInfo);
+            info.fields.push_back(diffInfo);
+
+            mUniforms->push_back(info);
+            mDepthRangeAdded = true;
+        }
+    }
+    else
+    {
+        switch (symbol->getQualifier())
+        {
+            case EvqAttribute:
+            case EvqVertexIn:
+                var = FindVariable(symbolName, mAttribs);
+                break;
+            case EvqFragmentOut:
+                var = FindVariable(symbolName, mOutputVariables);
+                break;
+            case EvqUniform:
+            {
+                const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
+                if (interfaceBlock)
+                {
+                    InterfaceBlock *namedBlock =
+                        FindVariable(interfaceBlock->name(), mUniformBlocks);
+                    ASSERT(namedBlock);
+                    var = FindVariable(symbolName, &namedBlock->fields);
+
+                    // Set static use on the parent interface block here
+                    namedBlock->staticUse = true;
+                }
+                else
+                {
+                    var = FindVariable(symbolName, mUniforms);
+                }
+
+                // It's an internal error to reference an undefined user uniform
+                ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
+            }
+            break;
+            case EvqFragCoord:
+                recordBuiltInVaryingUsed("gl_FragCoord", &mFragCoordAdded, mInputVaryings);
+                return;
+            case EvqFrontFacing:
+                recordBuiltInVaryingUsed("gl_FrontFacing", &mFrontFacingAdded, mInputVaryings);
+                return;
+            case EvqPointCoord:
+                recordBuiltInVaryingUsed("gl_PointCoord", &mPointCoordAdded, mInputVaryings);
+                return;
+            case EvqInstanceID:
+                // Whenever the SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW option is set,
+                // gl_InstanceID is added inside expressions to initialize ViewID_OVR and
+                // InstanceID. gl_InstanceID is not added to the symbol table for ESSL1 shaders
+                // which makes it necessary to populate the type information explicitly instead of
+                // extracting it from the symbol table.
+                if (!mInstanceIDAdded)
+                {
+                    Attribute info;
+                    const char kName[] = "gl_InstanceID";
+                    info.name          = kName;
+                    info.mappedName    = kName;
+                    info.type          = GL_INT;
+                    info.arraySize     = 0;
+                    info.precision     = GL_HIGH_INT;  // Defined by spec.
+                    info.staticUse     = true;
+                    info.location      = -1;
+                    mAttribs->push_back(info);
+                    mInstanceIDAdded = true;
+                }
+                return;
+            case EvqVertexID:
+                recordBuiltInAttributeUsed("gl_VertexID", &mVertexIDAdded);
+                return;
+            case EvqPosition:
+                recordBuiltInVaryingUsed("gl_Position", &mPositionAdded, mOutputVaryings);
+                return;
+            case EvqPointSize:
+                recordBuiltInVaryingUsed("gl_PointSize", &mPointSizeAdded, mOutputVaryings);
+                return;
+            case EvqLastFragData:
+                recordBuiltInVaryingUsed("gl_LastFragData", &mLastFragDataAdded, mInputVaryings);
+                return;
+            case EvqFragColor:
+                recordBuiltInFragmentOutputUsed("gl_FragColor", &mFragColorAdded);
+                return;
+            case EvqFragData:
+                if (!mFragDataAdded)
+                {
+                    OutputVariable info;
+                    setBuiltInInfoFromSymbolTable("gl_FragData", &info);
+                    if (!::IsExtensionEnabled(mExtensionBehavior, "GL_EXT_draw_buffers"))
+                    {
+                        info.arraySize = 1;
+                    }
+                    info.staticUse = true;
+                    mOutputVariables->push_back(info);
+                    mFragDataAdded = true;
+                }
+                return;
+            case EvqFragDepthEXT:
+                recordBuiltInFragmentOutputUsed("gl_FragDepthEXT", &mFragDepthEXTAdded);
+                return;
+            case EvqFragDepth:
+                recordBuiltInFragmentOutputUsed("gl_FragDepth", &mFragDepthAdded);
+                return;
+            case EvqSecondaryFragColorEXT:
+                recordBuiltInFragmentOutputUsed("gl_SecondaryFragColorEXT",
+                                                &mSecondaryFragColorEXTAdded);
+                return;
+            case EvqSecondaryFragDataEXT:
+                recordBuiltInFragmentOutputUsed("gl_SecondaryFragDataEXT",
+                                                &mSecondaryFragDataEXTAdded);
+                return;
+            default:
+                break;
+        }
+    }
+    if (var)
+    {
+        MarkStaticallyUsed(var);
+    }
+}
+
+void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
+                                                            const TString &name,
+                                                            ShaderVariable *variableOut) const
+{
+    ASSERT(variableOut);
+
+    const TStructure *structure = type.getStruct();
+
+    if (!structure)
+    {
+        variableOut->type      = GLVariableType(type);
+        variableOut->precision = GLVariablePrecision(type);
+    }
+    else
+    {
+        // Note: this enum value is not exposed outside ANGLE
+        variableOut->type       = GL_STRUCT_ANGLEX;
+        variableOut->structName = structure->name().c_str();
+
+        const TFieldList &fields = structure->fields();
+
+        for (TField *field : fields)
+        {
+            // Regardless of the variable type (uniform, in/out etc.) its fields are always plain
+            // ShaderVariable objects.
+            ShaderVariable fieldVariable;
+            setCommonVariableProperties(*field->type(), field->name(), &fieldVariable);
+            variableOut->fields.push_back(fieldVariable);
+        }
+    }
+    variableOut->name       = name.c_str();
+    variableOut->mappedName = HashName(name, mHashFunction).c_str();
+    variableOut->arraySize  = type.getArraySize();
+}
+
+Attribute CollectVariablesTraverser::recordAttribute(const TIntermSymbol &variable) const
+{
+    const TType &type = variable.getType();
+    ASSERT(!type.getStruct());
+
+    Attribute attribute;
+    setCommonVariableProperties(type, variable.getSymbol(), &attribute);
+
+    attribute.location = type.getLayoutQualifier().location;
+    return attribute;
+}
+
+OutputVariable CollectVariablesTraverser::recordOutputVariable(const TIntermSymbol &variable) const
+{
+    const TType &type = variable.getType();
+    ASSERT(!type.getStruct());
+
+    OutputVariable outputVariable;
+    setCommonVariableProperties(type, variable.getSymbol(), &outputVariable);
+
+    outputVariable.location = type.getLayoutQualifier().location;
+    return outputVariable;
+}
+
+Varying CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) const
+{
+    const TType &type = variable.getType();
+
+    Varying varying;
+    setCommonVariableProperties(type, variable.getSymbol(), &varying);
+
+    switch (type.getQualifier())
+    {
+        case EvqVaryingIn:
+        case EvqVaryingOut:
+        case EvqVertexOut:
+        case EvqSmoothOut:
+        case EvqFlatOut:
+        case EvqCentroidOut:
+            if (mSymbolTable->isVaryingInvariant(std::string(variable.getSymbol().c_str())) ||
+                type.isInvariant())
+            {
+                varying.isInvariant = true;
+            }
+            break;
+        default:
+            break;
+    }
+
+    varying.interpolation = GetInterpolationType(type.getQualifier());
+    return varying;
+}
+
+InterfaceBlock CollectVariablesTraverser::recordInterfaceBlock(const TIntermSymbol &variable) const
+{
+    const TInterfaceBlock *blockType = variable.getType().getInterfaceBlock();
+    ASSERT(blockType);
+
+    InterfaceBlock interfaceBlock;
+    interfaceBlock.name       = blockType->name().c_str();
+    interfaceBlock.mappedName = HashName(blockType->name().c_str(), mHashFunction).c_str();
+    interfaceBlock.instanceName =
+        (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
+    interfaceBlock.arraySize        = variable.getArraySize();
+    interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
+    interfaceBlock.binding          = blockType->blockBinding();
+    interfaceBlock.layout           = GetBlockLayoutType(blockType->blockStorage());
+    interfaceBlock.blockType        = GetBlockType(variable.getType().getQualifier());
+
+    // Gather field information
+    for (const TField *field : blockType->fields())
+    {
+        const TType &fieldType = *field->type();
+
+        InterfaceBlockField fieldVariable;
+        setCommonVariableProperties(fieldType, field->name(), &fieldVariable);
+        fieldVariable.isRowMajorLayout =
+            (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
+        interfaceBlock.fields.push_back(fieldVariable);
+    }
+    return interfaceBlock;
+}
+
+Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
+{
+    Uniform uniform;
+    setCommonVariableProperties(variable.getType(), variable.getSymbol(), &uniform);
+    uniform.binding  = variable.getType().getLayoutQualifier().binding;
+    uniform.location = variable.getType().getLayoutQualifier().location;
+    uniform.offset   = variable.getType().getLayoutQualifier().offset;
+    return uniform;
+}
+
+bool CollectVariablesTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+    const TIntermSequence &sequence = *(node->getSequence());
+    ASSERT(!sequence.empty());
+
+    const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
+    TQualifier qualifier          = typedNode.getQualifier();
+
+    bool isShaderVariable = qualifier == EvqAttribute || qualifier == EvqVertexIn ||
+                            qualifier == EvqFragmentOut || qualifier == EvqUniform ||
+                            IsVarying(qualifier);
+
+    if (typedNode.getBasicType() != EbtInterfaceBlock && !isShaderVariable)
+    {
+        return true;
+    }
+
+    for (TIntermNode *variableNode : sequence)
+    {
+        // The only case in which the sequence will not contain a TIntermSymbol node is
+        // initialization. It will contain a TInterBinary node in that case. Since attributes,
+        // uniforms, varyings, outputs and interface blocks cannot be initialized in a shader, we
+        // must have only TIntermSymbol nodes in the sequence in the cases we are interested in.
+        const TIntermSymbol &variable = *variableNode->getAsSymbolNode();
+        if (variable.getName().isInternal())
+        {
+            // Internal variables are not collected.
+            continue;
+        }
+
+        if (typedNode.getBasicType() == EbtInterfaceBlock)
+        {
+            if (qualifier == EvqUniform)
+            {
+                mUniformBlocks->push_back(recordInterfaceBlock(variable));
+            }
+            else if (qualifier == EvqBuffer)
+            {
+                mShaderStorageBlocks->push_back(recordInterfaceBlock(variable));
+            }
+        }
+        else
+        {
+            switch (qualifier)
+            {
+                case EvqAttribute:
+                case EvqVertexIn:
+                    mAttribs->push_back(recordAttribute(variable));
+                    break;
+                case EvqFragmentOut:
+                    mOutputVariables->push_back(recordOutputVariable(variable));
+                    break;
+                case EvqUniform:
+                    mUniforms->push_back(recordUniform(variable));
+                    break;
+                default:
+                    if (IsVaryingIn(qualifier))
+                    {
+                        mInputVaryings->push_back(recordVarying(variable));
+                    }
+                    else
+                    {
+                        ASSERT(IsVaryingOut(qualifier));
+                        mOutputVaryings->push_back(recordVarying(variable));
+                    }
+                    break;
+            }
+        }
+    }
+
+    // None of the recorded variables can have initializers, so we don't need to traverse the
+    // declarators.
+    return false;
+}
+
+bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
+{
+    if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
+    {
+        // NOTE: we do not determine static use for individual blocks of an array
+        TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
+        ASSERT(blockNode);
+
+        TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
+        ASSERT(constantUnion);
+
+        const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
+        InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mUniformBlocks);
+        if (!namedBlock)
+        {
+            namedBlock = FindVariable(interfaceBlock->name(), mShaderStorageBlocks);
+        }
+        ASSERT(namedBlock);
+        namedBlock->staticUse   = true;
+        unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
+        ASSERT(fieldIndex < namedBlock->fields.size());
+        namedBlock->fields[fieldIndex].staticUse = true;
+        return false;
+    }
+
+    return true;
+}
+
+}  // anonymous namespace
+
+void CollectVariables(TIntermBlock *root,
+                      std::vector<Attribute> *attributes,
+                      std::vector<OutputVariable> *outputVariables,
+                      std::vector<Uniform> *uniforms,
+                      std::vector<Varying> *inputVaryings,
+                      std::vector<Varying> *outputVaryings,
+                      std::vector<InterfaceBlock> *uniformBlocks,
+                      std::vector<InterfaceBlock> *shaderStorageBlocks,
+                      ShHashFunction64 hashFunction,
+                      TSymbolTable *symbolTable,
+                      int shaderVersion,
+                      const TExtensionBehavior &extensionBehavior)
+{
+    CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
+                                      outputVaryings, uniformBlocks, shaderStorageBlocks,
+                                      hashFunction, symbolTable, shaderVersion, extensionBehavior);
+    root->traverse(&collect);
+}
+
+}  // namespace sh