Add GLSL support for runtime-sized arrays in SSBOs

The GLSL parser now allows a runtime-sized array as the last member in
a shader storage block. Clamping indexing against the memory bounds is
done by determining the array length at runtime.

Runtime-sized arrays are used in dEQP tests for many compute shader
tests, so these now work on the OpenGL backend.

BUG=angleproject:1951
TEST=angle_unittests,
     dEQP-GLES31.functional.shaders.linkage.shader_storage_block.*
     dEQP-GLES31.functional.shaders.builtin_functions.*compute*

Change-Id: Ibecca24623ca8e4723af6f0e0421fe9711ea828d
Reviewed-on: https://chromium-review.googlesource.com/787976
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 9a47287..edaf2eb 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -526,24 +526,42 @@
                 }
                 else if (visit == PostVisit)
                 {
-                    int maxSize;
                     TIntermTyped *left = node->getLeft();
                     TType leftType     = left->getType();
 
-                    if (left->isArray())
-                    {
-                        // The shader will fail validation if the array length is not > 0.
-                        maxSize = static_cast<int>(leftType.getOutermostArraySize()) - 1;
-                    }
-                    else
-                    {
-                        maxSize = leftType.getNominalSize() - 1;
-                    }
-
                     if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
-                        out << "), 0.0, float(" << maxSize << ")))]";
+                        out << "), 0.0, float(";
                     else
-                        out << ", 0, " << maxSize << ")]";
+                        out << ", 0, ";
+
+                    if (leftType.isUnsizedArray())
+                    {
+                        // For runtime-sized arrays in ESSL 3.10 we need to call the length method
+                        // to get the length to clamp against. See ESSL 3.10 section 4.1.9. Note
+                        // that a runtime-sized array expression is guaranteed not to have side
+                        // effects, so it's fine to add the expression to the output twice.
+                        ASSERT(mShaderVersion >= 310);
+                        ASSERT(!left->hasSideEffects());
+                        left->traverse(this);
+                        out << ".length() - 1";
+                    }
+                    else
+                    {
+                        int maxSize;
+                        if (leftType.isArray())
+                        {
+                            maxSize = static_cast<int>(leftType.getOutermostArraySize()) - 1;
+                        }
+                        else
+                        {
+                            maxSize = leftType.getNominalSize() - 1;
+                        }
+                        out << maxSize;
+                    }
+                    if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
+                        out << ")))]";
+                    else
+                        out << ")]";
                 }
             }
             else