ES31: Implement gl_in in Geometry Shader

This patch intends to implement geometry shader built-in interface
block instance gl_in defined in GL_OES_geometry_shader.

1. Add the definition of gl_in and its interface block gl_PerVertex
   into the symbol table.
2. Support gl_Position as a member of gl_in.
3. Set the array size of gl_in when a valid input primitive type is
   known.
4. Add check that it should be a compile error to index gl_in or
   call length() on gl_in without a valid input primitive declaration.

This patch also adds unit tests to cover all these new features.

BUG=angleproject:1941
TEST=angle_unittests

Change-Id: I8da20c943b29c9ce904834625b396aab6302e1e1
Reviewed-on: https://chromium-review.googlesource.com/605059
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index 336f3b7..00f1108 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -576,6 +576,7 @@
     // GLSL ES 3.1 extension OES_geometry_shader qualifiers
     EvqGeometryIn,
     EvqGeometryOut,
+    EvqPerVertexIn,
     EvqLayer,  // gl_Layer
 
     // end of list
@@ -832,6 +833,7 @@
     case EvqWriteOnly:              return "writeonly";
     case EvqGeometryIn:             return "in";
     case EvqGeometryOut:            return "out";
+    case EvqPerVertexIn:            return "gl_in";
     default: UNREACHABLE();         return "unknown qualifier";
     }
     // clang-format on
diff --git a/src/compiler/translator/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
index 4ffae59..5815940 100644
--- a/src/compiler/translator/CollectVariables.cpp
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -36,6 +36,7 @@
     }
 }
 
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
 BlockType GetBlockType(TQualifier qualifier)
 {
     switch (qualifier)
@@ -44,6 +45,8 @@
             return BlockType::BLOCK_UNIFORM;
         case EvqBuffer:
             return BlockType::BLOCK_BUFFER;
+        case EvqPerVertexIn:
+            return BlockType::BLOCK_IN;
         default:
             UNREACHABLE();
             return BlockType::BLOCK_UNIFORM;
@@ -93,6 +96,7 @@
                               std::vector<Varying> *outputVaryings,
                               std::vector<InterfaceBlock> *uniformBlocks,
                               std::vector<InterfaceBlock> *shaderStorageBlocks,
+                              std::vector<InterfaceBlock> *inBlocks,
                               ShHashFunction64 hashFunction,
                               TSymbolTable *symbolTable,
                               int shaderVersion,
@@ -110,7 +114,8 @@
     Attribute recordAttribute(const TIntermSymbol &variable) const;
     OutputVariable recordOutputVariable(const TIntermSymbol &variable) const;
     Varying recordVarying(const TIntermSymbol &variable) const;
-    InterfaceBlock recordInterfaceBlock(const TIntermSymbol &variable) const;
+    void recordInterfaceBlock(const TType &interfaceBlockType,
+                              InterfaceBlock *interfaceBlock) const;
     Uniform recordUniform(const TIntermSymbol &variable) const;
 
     void setBuiltInInfoFromSymbolTable(const char *name, ShaderVariable *info);
@@ -120,6 +125,8 @@
                                   std::vector<Varying> *varyings);
     void recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag);
     void recordBuiltInAttributeUsed(const char *name, bool *addedFlag);
+    InterfaceBlock *recordGLInUsed(const TType &glInType);
+    InterfaceBlock *findNamedInterfaceBlock(const TString &name) const;
 
     std::vector<Attribute> *mAttribs;
     std::vector<OutputVariable> *mOutputVariables;
@@ -128,6 +135,7 @@
     std::vector<Varying> *mOutputVaryings;
     std::vector<InterfaceBlock> *mUniformBlocks;
     std::vector<InterfaceBlock> *mShaderStorageBlocks;
+    std::vector<InterfaceBlock> *mInBlocks;
 
     std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
 
@@ -148,6 +156,8 @@
     bool mSecondaryFragColorEXTAdded;
     bool mSecondaryFragDataEXTAdded;
 
+    bool mPerVertexInAdded;
+
     ShHashFunction64 mHashFunction;
 
     int mShaderVersion;
@@ -162,6 +172,7 @@
     std::vector<sh::Varying> *outputVaryings,
     std::vector<sh::InterfaceBlock> *uniformBlocks,
     std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
+    std::vector<sh::InterfaceBlock> *inBlocks,
     ShHashFunction64 hashFunction,
     TSymbolTable *symbolTable,
     int shaderVersion,
@@ -174,6 +185,7 @@
       mOutputVaryings(outputVaryings),
       mUniformBlocks(uniformBlocks),
       mShaderStorageBlocks(shaderStorageBlocks),
+      mInBlocks(inBlocks),
       mDepthRangeAdded(false),
       mPointCoordAdded(false),
       mFrontFacingAdded(false),
@@ -189,6 +201,7 @@
       mFragDepthAdded(false),
       mSecondaryFragColorEXTAdded(false),
       mSecondaryFragDataEXTAdded(false),
+      mPerVertexInAdded(false),
       mHashFunction(hashFunction),
       mShaderVersion(shaderVersion),
       mExtensionBehavior(extensionBehavior)
@@ -251,6 +264,25 @@
     }
 }
 
+InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
+{
+    if (!mPerVertexInAdded)
+    {
+        ASSERT(glInType.getQualifier() == EvqPerVertexIn);
+        InterfaceBlock info;
+        recordInterfaceBlock(glInType, &info);
+        info.staticUse = true;
+
+        mPerVertexInAdded = true;
+        mInBlocks->push_back(info);
+        return &mInBlocks->back();
+    }
+    else
+    {
+        return FindVariable("gl_PerVertex", mInBlocks);
+    }
+}
+
 // 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
@@ -535,21 +567,30 @@
     return varying;
 }
 
-InterfaceBlock CollectVariablesTraverser::recordInterfaceBlock(const TIntermSymbol &variable) const
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+void CollectVariablesTraverser::recordInterfaceBlock(const TType &interfaceBlockType,
+                                                     InterfaceBlock *interfaceBlock) const
 {
-    const TInterfaceBlock *blockType = variable.getType().getInterfaceBlock();
+    ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock);
+    ASSERT(interfaceBlock);
+
+    const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
     ASSERT(blockType);
 
-    InterfaceBlock interfaceBlock;
-    interfaceBlock.name       = blockType->name().c_str();
-    interfaceBlock.mappedName = HashName(blockType->name().c_str(), mHashFunction).c_str();
-    interfaceBlock.instanceName =
+    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());
+    interfaceBlock->arraySize = interfaceBlockType.getArraySize();
+
+    interfaceBlock->blockType = GetBlockType(interfaceBlockType.getQualifier());
+    if (interfaceBlock->blockType == BlockType::BLOCK_UNIFORM ||
+        interfaceBlock->blockType == BlockType::BLOCK_BUFFER)
+    {
+        interfaceBlock->isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
+        interfaceBlock->binding          = blockType->blockBinding();
+        interfaceBlock->layout           = GetBlockLayoutType(blockType->blockStorage());
+    }
 
     // Gather field information
     for (const TField *field : blockType->fields())
@@ -560,9 +601,8 @@
         setCommonVariableProperties(fieldType, field->name(), &fieldVariable);
         fieldVariable.isRowMajorLayout =
             (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
-        interfaceBlock.fields.push_back(fieldVariable);
+        interfaceBlock->fields.push_back(fieldVariable);
     }
-    return interfaceBlock;
 }
 
 Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
@@ -605,15 +645,22 @@
             continue;
         }
 
+        // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
         if (typedNode.getBasicType() == EbtInterfaceBlock)
         {
-            if (qualifier == EvqUniform)
+            InterfaceBlock interfaceBlock;
+            recordInterfaceBlock(variable.getType(), &interfaceBlock);
+
+            switch (qualifier)
             {
-                mUniformBlocks->push_back(recordInterfaceBlock(variable));
-            }
-            else if (qualifier == EvqBuffer)
-            {
-                mShaderStorageBlocks->push_back(recordInterfaceBlock(variable));
+                case EvqUniform:
+                    mUniformBlocks->push_back(interfaceBlock);
+                    break;
+                case EvqBuffer:
+                    mShaderStorageBlocks->push_back(interfaceBlock);
+                    break;
+                default:
+                    UNREACHABLE();
             }
         }
         else
@@ -650,6 +697,18 @@
     return false;
 }
 
+// TODO(jiawei.shao@intel.com): add search on mInBlocks and mOutBlocks when implementing
+// GL_OES_shader_io_blocks.
+InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(const TString &blockName) const
+{
+    InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks);
+    if (!namedBlock)
+    {
+        namedBlock = FindVariable(blockName, mShaderStorageBlocks);
+    }
+    return namedBlock;
+}
+
 bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
 {
     if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
@@ -661,17 +720,43 @@
         TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
         ASSERT(constantUnion);
 
+        InterfaceBlock *namedBlock = nullptr;
+
+        bool traverseIndexExpression         = false;
+        TIntermBinary *interfaceIndexingNode = blockNode->getAsBinaryNode();
+        if (interfaceIndexingNode)
+        {
+            TIntermTyped *interfaceNode = interfaceIndexingNode->getLeft()->getAsTyped();
+            ASSERT(interfaceNode);
+
+            const TType &interfaceType = interfaceNode->getType();
+            if (interfaceType.getQualifier() == EvqPerVertexIn)
+            {
+                namedBlock = recordGLInUsed(interfaceType);
+                ASSERT(namedBlock);
+
+                // We need to continue traversing to collect useful variables in the index
+                // expression of gl_in.
+                traverseIndexExpression = true;
+            }
+        }
+
         const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
-        InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mUniformBlocks);
         if (!namedBlock)
         {
-            namedBlock = FindVariable(interfaceBlock->name(), mShaderStorageBlocks);
+            namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
         }
         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;
+
+        if (traverseIndexExpression)
+        {
+            ASSERT(interfaceIndexingNode);
+            interfaceIndexingNode->getRight()->traverse(this);
+        }
         return false;
     }
 
@@ -688,13 +773,14 @@
                       std::vector<Varying> *outputVaryings,
                       std::vector<InterfaceBlock> *uniformBlocks,
                       std::vector<InterfaceBlock> *shaderStorageBlocks,
+                      std::vector<InterfaceBlock> *inBlocks,
                       ShHashFunction64 hashFunction,
                       TSymbolTable *symbolTable,
                       int shaderVersion,
                       const TExtensionBehavior &extensionBehavior)
 {
     CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
-                                      outputVaryings, uniformBlocks, shaderStorageBlocks,
+                                      outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
                                       hashFunction, symbolTable, shaderVersion, extensionBehavior);
     root->traverse(&collect);
 }
diff --git a/src/compiler/translator/CollectVariables.h b/src/compiler/translator/CollectVariables.h
index 3a40824..fb257f9 100644
--- a/src/compiler/translator/CollectVariables.h
+++ b/src/compiler/translator/CollectVariables.h
@@ -26,6 +26,7 @@
                       std::vector<Varying> *outputVaryings,
                       std::vector<InterfaceBlock> *uniformBlocks,
                       std::vector<InterfaceBlock> *shaderStorageBlocks,
+                      std::vector<InterfaceBlock> *inBlocks,
                       ShHashFunction64 hashFunction,
                       TSymbolTable *symbolTable,
                       int shaderVersion,
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index ef8141f..8ef6285 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -475,8 +475,8 @@
         {
             ASSERT(!variablesCollected);
             CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
-                             &outputVaryings, &uniformBlocks, &shaderStorageBlocks, hashFunction,
-                             &symbolTable, shaderVersion, extensionBehavior);
+                             &outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
+                             hashFunction, &symbolTable, shaderVersion, extensionBehavior);
             collectInterfaceBlocks();
             variablesCollected = true;
             if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
@@ -751,10 +751,11 @@
 void TCompiler::collectInterfaceBlocks()
 {
     ASSERT(interfaceBlocks.empty());
-    interfaceBlocks.reserve(uniformBlocks.size() + shaderStorageBlocks.size());
+    interfaceBlocks.reserve(uniformBlocks.size() + shaderStorageBlocks.size() + inBlocks.size());
     interfaceBlocks.insert(interfaceBlocks.end(), uniformBlocks.begin(), uniformBlocks.end());
     interfaceBlocks.insert(interfaceBlocks.end(), shaderStorageBlocks.begin(),
                            shaderStorageBlocks.end());
+    interfaceBlocks.insert(interfaceBlocks.end(), inBlocks.begin(), inBlocks.end());
 }
 
 void TCompiler::clearResults()
@@ -773,6 +774,7 @@
     interfaceBlocks.clear();
     uniformBlocks.clear();
     shaderStorageBlocks.clear();
+    inBlocks.clear();
     variablesCollected = false;
     mGLPositionInitialized = false;
 
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index 54f1e38..085b7a9 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -119,6 +119,7 @@
     {
         return shaderStorageBlocks;
     }
+    const std::vector<sh::InterfaceBlock> &getInBlocks() const { return inBlocks; }
 
     ShHashFunction64 getHashFunction() const { return hashFunction; }
     NameMap &getNameMap() { return nameMap; }
@@ -196,6 +197,7 @@
     std::vector<sh::InterfaceBlock> interfaceBlocks;
     std::vector<sh::InterfaceBlock> uniformBlocks;
     std::vector<sh::InterfaceBlock> shaderStorageBlocks;
+    std::vector<sh::InterfaceBlock> inBlocks;
 
   private:
     // Creates the function call DAG for further analysis, returning false if there is a recursion
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index c362ae2..61acd4f 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -908,9 +908,32 @@
         }
 
         case GL_GEOMETRY_SHADER_OES:
-            // TODO(jiawei.shao@intel.com): add Geometry Shader built-in variables.
-            break;
+        {
+            // TODO(jiawei.shao@intel.com): add all Geometry Shader built-in variables.
+            const char *extension = "GL_OES_geometry_shader";
 
+            // Add built-in interface block gl_PerVertex and the built-in array gl_in.
+            // TODO(jiawei.shao@intel.com): implement GL_OES_geometry_point_size.
+            const TString *glPerVertexString = NewPoolTString("gl_PerVertex");
+            symbolTable.insertInterfaceBlockNameExt(ESSL3_1_BUILTINS, extension, glPerVertexString);
+
+            TFieldList *fieldList    = NewPoolTFieldList();
+            TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
+            TField *glPositionField  = new TField(new TType(EbtFloat, EbpHigh, EvqPosition, 4),
+                                                 NewPoolTString("gl_Position"), zeroSourceLoc);
+            fieldList->push_back(glPositionField);
+
+            TInterfaceBlock *glInBlock = new TInterfaceBlock(
+                glPerVertexString, fieldList, NewPoolTString("gl_in"), TLayoutQualifier::create());
+
+            // The array size of gl_in is undefined until we get a valid input primitive
+            // declaration.
+            TType glInType(glInBlock, EvqPerVertexIn, TLayoutQualifier::create(), 0);
+            glInType.setArrayUnsized();
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_in", glInType);
+
+            break;
+        }
         default:
             UNREACHABLE();
     }
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index b7aacee..29da221 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -622,7 +622,8 @@
                 const TField *field               = interfaceBlock->fields()[index->getIConst(0)];
 
                 TString fieldName = field->name();
-                ASSERT(!mSymbolTable->findBuiltIn(interfaceBlock->name(), mShaderVersion));
+                ASSERT(!mSymbolTable->findBuiltIn(interfaceBlock->name(), mShaderVersion) ||
+                       interfaceBlock->name() == "gl_PerVertex");
                 fieldName = hashName(TName(fieldName));
 
                 out << fieldName;
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index f1cb9ed..25ee442 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -95,6 +95,27 @@
     return true;
 }
 
+// Map input primitive types to input array sizes in a geometry shader.
+GLuint GetGeometryShaderInputArraySize(TLayoutPrimitiveType primitiveType)
+{
+    switch (primitiveType)
+    {
+        case EptPoints:
+            return 1u;
+        case EptLines:
+            return 2u;
+        case EptTriangles:
+            return 3u;
+        case EptLinesAdjacency:
+            return 4u;
+        case EptTrianglesAdjacency:
+            return 6u;
+        default:
+            UNREACHABLE();
+            return 0u;
+    }
+}
+
 }  // namespace
 
 // This tracks each binding point's current default offset for inheritance of subsequent
@@ -184,7 +205,8 @@
       mGeometryShaderInvocations(0),
       mGeometryShaderMaxVertices(-1),
       mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
-      mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices)
+      mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices),
+      mGeometryShaderInputArraySize(0)
 {
     mComputeShaderLocalSize.fill(-1);
 }
@@ -496,6 +518,9 @@
         case EvqComputeIn:
             message = "can't modify work group size variable";
             break;
+        case EvqPerVertexIn:
+            message = "can't modify any member in gl_in";
+            break;
         default:
             //
             // Type that can't be written to?
@@ -1748,6 +1773,13 @@
         type.setQualifier(EvqConst);
         node = new TIntermConstantUnion(constArray, type);
     }
+    // TODO(jiawei.shao@intel.com): set array sizes for user-defined geometry shader inputs.
+    else if (variable->getType().getQualifier() == EvqPerVertexIn)
+    {
+        TType type(variable->getType());
+        type.setArraySize(mGeometryShaderInputArraySize);
+        node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), type);
+    }
     else
     {
         node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType());
@@ -2683,6 +2715,15 @@
     }
 }
 
+void TParseContext::setGeometryShaderInputArraySizes()
+{
+    // TODO(jiawei.shao@intel.com): check former input array sizes match the input primitive
+    // declaration.
+    ASSERT(mGeometryShaderInputArraySize == 0);
+    mGeometryShaderInputArraySize =
+        GetGeometryShaderInputArraySize(mGeometryShaderInputPrimitiveType);
+}
+
 bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier)
 {
     ASSERT(typeQualifier.qualifier == EvqGeometryIn);
@@ -2708,6 +2749,7 @@
         if (mGeometryShaderInputPrimitiveType == EptUndefined)
         {
             mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType;
+            setGeometryShaderInputArraySizes();
         }
         else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType)
         {
@@ -3337,6 +3379,7 @@
 
 //
 // Interface/uniform blocks
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
 //
 TIntermDeclaration *TParseContext::addInterfaceBlock(
     const TTypeQualifierBuilder &typeQualifierBuilder,
@@ -3641,6 +3684,16 @@
         return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
     }
 
+    if (baseExpression->getQualifier() == EvqPerVertexIn)
+    {
+        ASSERT(mShaderType == GL_GEOMETRY_SHADER_OES);
+        if (mGeometryShaderInputPrimitiveType == EptUndefined)
+        {
+            error(location, "missing input primitive declaration before indexing gl_in.", "[");
+            return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
+        }
+    }
+
     TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
 
     // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able
@@ -3651,9 +3704,21 @@
     {
         if (baseExpression->isInterfaceBlock())
         {
-            error(location,
-                  "array indexes for interface blocks arrays must be constant integral expressions",
-                  "[");
+            // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+            switch (baseExpression->getQualifier())
+            {
+                case EvqPerVertexIn:
+                    break;
+                case EvqUniform:
+                case EvqBuffer:
+                    error(location,
+                          "array indexes for uniform block arrays and shader storage block arrays "
+                          "must be constant integral expressions",
+                          "[");
+                    break;
+                default:
+                    UNREACHABLE();
+            }
         }
         else if (baseExpression->getQualifier() == EvqFragmentOut)
         {
@@ -5326,6 +5391,11 @@
     {
         error(loc, "length can only be called on arrays", "length");
     }
+    else if (typedThis->getQualifier() == EvqPerVertexIn &&
+             mGeometryShaderInputPrimitiveType == EptUndefined)
+    {
+        error(loc, "missing input primitive declaration before calling length on gl_in", "length");
+    }
     else
     {
         arraySize = typedThis->getArraySize();
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 0ea5535..e9b2c08 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -535,6 +535,7 @@
     bool checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier);
     bool parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);
     bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
+    void setGeometryShaderInputArraySizes();
 
     // Set to true when the last/current declarator list was started with an empty declaration. The
     // non-empty declaration error check will need to be performed if the empty declaration is
@@ -600,6 +601,8 @@
     int mGeometryShaderMaxVertices;
     int mMaxGeometryShaderInvocations;
     int mMaxGeometryShaderMaxVertices;
+    int mGeometryShaderInputArraySize;  // Track if all input array sizes are same and matches the
+                                        // latter input primitive declaration.
 };
 
 int PaParseStrings(size_t count,
diff --git a/src/compiler/translator/SymbolTable.cpp b/src/compiler/translator/SymbolTable.cpp
index 38a87ed..5d15c9b 100644
--- a/src/compiler/translator/SymbolTable.cpp
+++ b/src/compiler/translator/SymbolTable.cpp
@@ -299,6 +299,18 @@
     return nullptr;
 }
 
+TInterfaceBlockName *TSymbolTable::insertInterfaceBlockNameExt(ESymbolLevel level,
+                                                               const char *ext,
+                                                               const TString *name)
+{
+    TInterfaceBlockName *blockNameSymbol = new TInterfaceBlockName(this, name);
+    if (insert(level, ext, blockNameSymbol))
+    {
+        return blockNameSymbol;
+    }
+    return nullptr;
+}
+
 TVariable *TSymbolTable::insertVariable(ESymbolLevel level, const char *name, const TType &type)
 {
     return insertVariable(level, NewPoolTString(name), type);
diff --git a/src/compiler/translator/SymbolTable.h b/src/compiler/translator/SymbolTable.h
index 673bf0e..6d9b714 100644
--- a/src/compiler/translator/SymbolTable.h
+++ b/src/compiler/translator/SymbolTable.h
@@ -347,6 +347,9 @@
                                  const char *name,
                                  const TType &type);
     TVariable *insertStructType(ESymbolLevel level, TStructure *str);
+    TInterfaceBlockName *insertInterfaceBlockNameExt(ESymbolLevel level,
+                                                     const char *ext,
+                                                     const TString *name);
 
     bool insertConstInt(ESymbolLevel level, const char *name, int value, TPrecision precision)
     {
diff --git a/src/compiler/translator/Types.h b/src/compiler/translator/Types.h
index e120cd4..4450a0b 100644
--- a/src/compiler/translator/Types.h
+++ b/src/compiler/translator/Types.h
@@ -253,7 +253,7 @@
     TType(TInterfaceBlock *interfaceBlockIn,
           TQualifier qualifierIn,
           TLayoutQualifier layoutQualifierIn,
-          int arraySizeIn)
+          unsigned int arraySizeIn)
         : type(EbtInterfaceBlock),
           precision(EbpUndefined),
           qualifier(qualifierIn),
@@ -349,6 +349,7 @@
             invalidateMangledName();
         }
     }
+    void setArrayUnsized() { setArraySize(0u); }
     void clearArrayness()
     {
         if (array)