Add compute shader compilation support in the glsl compiler

Support is added for compute shader compilation. There is a small
extension to the parser so that 'local_size_x = ', 'local_size_y = '
and 'local_size_z = ' are supported as layout qualifiers.

A few shader compilation tests are added and one which checks the AST
whether the layout qualifiers are properly parsed.

BUG=angleproject:1442

TEST=angle_unittests
TEST=angle_end2end_tests

Change-Id: I67283797d1cf13fa4ac47faa2a6e66d93a2db867
Reviewed-on: https://chromium-review.googlesource.com/362300
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index cccde03..198ca36 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -7,6 +7,9 @@
 #ifndef COMPILER_TRANSLATOR_BASETYPES_H_
 #define COMPILER_TRANSLATOR_BASETYPES_H_
 
+#include <algorithm>
+#include <array>
+
 #include "common/debug.h"
 
 //
@@ -346,6 +349,9 @@
     EvqFlatIn,
     EvqCentroidIn,  // Implies smooth
 
+    // GLSL ES 3.1 compute shader special variables
+    EvqComputeIn,
+
     // end of list
     EvqLast
 };
@@ -365,12 +371,18 @@
     EbsStd140
 };
 
+using TLocalSize = std::array<int, 3>;
+
 struct TLayoutQualifier
 {
     int location;
     TLayoutMatrixPacking matrixPacking;
     TLayoutBlockStorage blockStorage;
 
+    // Compute shader layout qualifiers.
+    // -1 means unspecified.
+    TLocalSize localSize;
+
     static TLayoutQualifier create()
     {
         TLayoutQualifier layoutQualifier;
@@ -379,15 +391,66 @@
         layoutQualifier.matrixPacking = EmpUnspecified;
         layoutQualifier.blockStorage = EbsUnspecified;
 
+        layoutQualifier.localSize.fill(-1);
+
         return layoutQualifier;
     }
 
     bool isEmpty() const
     {
-        return location == -1 && matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified;
+        return location == -1 && matrixPacking == EmpUnspecified &&
+               blockStorage == EbsUnspecified && localSize[0] == -1 && localSize[1] == -1 &&
+               localSize[2] == -1;
+    }
+
+    bool isGroupSizeSpecified() const
+    {
+        return std::any_of(localSize.begin(), localSize.end(),
+                           [](int value) { return value != -1; });
+    }
+
+    bool isCombinationValid() const
+    {
+        bool workSizeSpecified = isGroupSizeSpecified();
+        bool otherLayoutQualifiersSpecified =
+            (location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified);
+
+        // we can have either the work group size specified, or the other layout qualifiers
+        return !(workSizeSpecified && otherLayoutQualifiersSpecified);
+    }
+
+    bool isLocalSizeEqual(const TLocalSize &localSizeIn) const
+    {
+        for (size_t i = 0u; i < localSize.size(); ++i)
+        {
+            bool result =
+                (localSize[i] == localSizeIn[i] || (localSize[i] == 1 && localSizeIn[i] == -1) ||
+                 (localSize[i] == -1 && localSizeIn[i] == 1));
+            if (!result)
+            {
+                return false;
+            }
+        }
+        return true;
     }
 };
 
+inline const char *getLocalSizeString(size_t dimension)
+{
+    switch (dimension)
+    {
+        case 0u:
+            return "local_size_x";
+        case 1u:
+            return "local_size_y";
+        case 2u:
+            return "local_size_z";
+        default:
+            UNREACHABLE();
+            return "dimension out of bounds";
+    }
+}
+
 //
 // This is just for debug print out, carried along with the definitions above.
 //
@@ -432,6 +495,7 @@
     case EvqSmoothIn:               return "smooth in";
     case EvqFlatIn:                 return "flat in";
     case EvqCentroidIn:             return "smooth centroid in";
+    case EvqComputeIn:              return "in";
     default: UNREACHABLE();         return "unknown qualifier";
     }
     // clang-format on
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index f586c7f..1469a33 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -150,8 +150,10 @@
       clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
       builtInFunctionEmulator(),
       mSourcePath(NULL),
+      mComputeShaderLocalSizeDeclared(false),
       mTemporaryIndex(0)
 {
+    mComputeShaderLocalSize.fill(1);
 }
 
 TCompiler::~TCompiler()
@@ -250,6 +252,9 @@
         mPragma = parseContext.pragma();
         symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
 
+        mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
+        mComputeShaderLocalSize         = parseContext.getComputeShaderLocalSize();
+
         root = parseContext.getTreeRoot();
         root = intermediate.postProcess(root);
 
@@ -450,6 +455,10 @@
         symbolTable.setDefaultPrecision(integer, EbpHigh);
         symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
         break;
+      case GL_COMPUTE_SHADER:
+          symbolTable.setDefaultPrecision(integer, EbpHigh);
+          symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
+          break;
       default:
         assert(false && "Language not supported");
     }
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index 4657daa..b982556 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -85,6 +85,9 @@
     int getShaderVersion() const { return shaderVersion; }
     TInfoSink& getInfoSink() { return infoSink; }
 
+    bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+    const TLocalSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
+
     // Clears the results from the previous compilation.
     void clearResults();
 
@@ -227,6 +230,10 @@
     TInfoSink infoSink;  // Output sink.
     const char *mSourcePath; // Path of source file or NULL
 
+    // compute shader local group size
+    bool mComputeShaderLocalSizeDeclared;
+    TLocalSize mComputeShaderLocalSize;
+
     // name hashing.
     ShHashFunction64 hashFunction;
     NameMap nameMap;
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 87dc8d6..76bed47 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -682,7 +682,9 @@
         symbolTable.insert(ESSL3_BUILTINS, new TVariable(NewPoolTString("gl_VertexID"),
                                                          TType(EbtInt, EbpHigh, EvqVertexID, 1)));
         break;
-
+      case GL_COMPUTE_SHADER:
+          // TODO (mradev): add compute shader built-ins
+          break;
       default:
         assert(false && "Language not supported");
     }
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index a96c4b6..33ec8c0 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -340,6 +340,9 @@
         case EvqPointCoord:
             message = "can't modify gl_PointCoord";
             break;
+        case EvqComputeIn:
+            message = "can't modify work group size variable";
+            break;
         default:
             //
             // Type that can't be written to?
@@ -1010,6 +1013,7 @@
         case EvqAttribute:
         case EvqVertexIn:
         case EvqFragmentOut:
+        case EvqComputeIn:
             if (publicType.type == EbtStruct)
             {
                 error(identifierLocation, "cannot be used with a structure",
@@ -1068,6 +1072,35 @@
     return false;
 }
 
+void TParseContext::layoutSupportedErrorCheck(const TSourceLoc &location,
+                                              const TString &layoutQualifierName,
+                                              int versionRequired)
+{
+
+    if (mShaderVersion < versionRequired)
+    {
+        error(location, "invalid layout qualifier:", layoutQualifierName.c_str(), "not supported");
+        recover();
+    }
+}
+
+bool TParseContext::layoutWorkGroupSizeErrorCheck(const TSourceLoc &location,
+                                                  const TLayoutQualifier &layoutQualifier)
+{
+    const TLocalSize &localSize = layoutQualifier.localSize;
+    for (size_t i = 0u; i < localSize.size(); ++i)
+    {
+        if (localSize[i] != -1)
+        {
+            error(location, "invalid layout qualifier:", getLocalSizeString(i),
+                  "only valid when used with 'in' in a compute shader global layout declaration");
+            return true;
+        }
+    }
+
+    return false;
+}
+
 bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
                                                  TIntermAggregate *aggregate)
 {
@@ -1132,6 +1165,23 @@
     mDirectiveHandler.handlePragma(srcLoc, name, value, stdgl);
 }
 
+TLocalSize TParseContext::getComputeShaderLocalSize() const
+{
+    TLocalSize result;
+    for (size_t i = 0u; i < result.size(); ++i)
+    {
+        if (mComputeShaderLocalSizeDeclared && mComputeShaderLocalSize[i] == -1)
+        {
+            result[i] = 1;
+        }
+        else
+        {
+            result[i] = mComputeShaderLocalSize[i];
+        }
+    }
+    return result;
+}
+
 /////////////////////////////////////////////////////////////////////////////////
 //
 // Non-Errors.
@@ -1386,6 +1436,11 @@
     returnType.invariant       = invariant;
     returnType.layoutQualifier = layoutQualifier;
 
+    if (layoutWorkGroupSizeErrorCheck(typeSpecifier.line, layoutQualifier))
+    {
+        recover();
+    }
+
     if (mShaderVersion < 300)
     {
         if (typeSpecifier.array)
@@ -1422,6 +1477,12 @@
         {
             es3InputOutputTypeCheck(qualifier, typeSpecifier, typeSpecifier.line);
         }
+        if (qualifier == EvqComputeIn)
+        {
+            error(typeSpecifier.line, "'in' can be only used to specify the local group size",
+                  "in");
+            recover();
+        }
     }
 
     return returnType;
@@ -1890,13 +1951,6 @@
 
 void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
 {
-    if (typeQualifier.qualifier != EvqUniform)
-    {
-        error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier),
-              "global layout must be uniform");
-        recover();
-        return;
-    }
 
     const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
 
@@ -1908,27 +1962,108 @@
         return;
     }
 
-    if (mShaderVersion < 300)
+    if (!layoutQualifier.isCombinationValid())
     {
-        error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 only", "layout");
+        error(typeQualifier.line, "invalid combination:", "layout");
         recover();
         return;
     }
 
-    if (layoutLocationErrorCheck(typeQualifier.line, typeQualifier.layoutQualifier))
+    if (typeQualifier.qualifier == EvqComputeIn)
     {
-        recover();
-        return;
-    }
+        if (mComputeShaderLocalSizeDeclared &&
+            !layoutQualifier.isLocalSizeEqual(mComputeShaderLocalSize))
+        {
+            error(typeQualifier.line, "Work group size does not match the previous declaration",
+                  "layout");
+            recover();
+            return;
+        }
 
-    if (layoutQualifier.matrixPacking != EmpUnspecified)
-    {
-        mDefaultMatrixPacking = layoutQualifier.matrixPacking;
-    }
+        if (mShaderVersion < 310)
+        {
+            error(typeQualifier.line, "in type qualifier supported in GLSL ES 3.10 only", "layout");
+            recover();
+            return;
+        }
 
-    if (layoutQualifier.blockStorage != EbsUnspecified)
+        if (!layoutQualifier.isGroupSizeSpecified())
+        {
+            error(typeQualifier.line, "No local work group size specified", "layout");
+            recover();
+            return;
+        }
+
+        const TVariable *maxComputeWorkGroupSize = static_cast<const TVariable *>(
+            symbolTable.findBuiltIn("gl_MaxComputeWorkGroupSize", mShaderVersion));
+
+        const TConstantUnion *maxComputeWorkGroupSizeData =
+            maxComputeWorkGroupSize->getConstPointer();
+
+        for (size_t i = 0u; i < layoutQualifier.localSize.size(); ++i)
+        {
+            if (layoutQualifier.localSize[i] != -1)
+            {
+                mComputeShaderLocalSize[i]             = layoutQualifier.localSize[i];
+                const int maxComputeWorkGroupSizeValue = maxComputeWorkGroupSizeData[i].getIConst();
+                if (mComputeShaderLocalSize[i] < 1 ||
+                    mComputeShaderLocalSize[i] > maxComputeWorkGroupSizeValue)
+                {
+                    std::stringstream errorMessageStream;
+                    errorMessageStream << "Value must be at least 1 and no greater than "
+                                       << maxComputeWorkGroupSizeValue;
+                    const std::string &errorMessage = errorMessageStream.str();
+
+                    error(typeQualifier.line, "invalid value:", getLocalSizeString(i),
+                          errorMessage.c_str());
+                    recover();
+                    return;
+                }
+            }
+        }
+
+        mComputeShaderLocalSizeDeclared = true;
+    }
+    else
     {
-        mDefaultBlockStorage = layoutQualifier.blockStorage;
+
+        if (layoutWorkGroupSizeErrorCheck(typeQualifier.line, typeQualifier.layoutQualifier))
+        {
+            recover();
+            return;
+        }
+
+        if (typeQualifier.qualifier != EvqUniform)
+        {
+            error(typeQualifier.line, "invalid qualifier:",
+                  getQualifierString(typeQualifier.qualifier), "global layout must be uniform");
+            recover();
+            return;
+        }
+
+        if (mShaderVersion < 300)
+        {
+            error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 and above",
+                  "layout");
+            recover();
+            return;
+        }
+
+        if (layoutLocationErrorCheck(typeQualifier.line, typeQualifier.layoutQualifier))
+        {
+            recover();
+            return;
+        }
+
+        if (layoutQualifier.matrixPacking != EmpUnspecified)
+        {
+            mDefaultMatrixPacking = layoutQualifier.matrixPacking;
+        }
+
+        if (layoutQualifier.blockStorage != EbsUnspecified)
+        {
+            mDefaultBlockStorage = layoutQualifier.blockStorage;
+        }
     }
 }
 
@@ -2463,6 +2598,11 @@
         blockLayoutQualifier.blockStorage = mDefaultBlockStorage;
     }
 
+    if (layoutWorkGroupSizeErrorCheck(nameLine, blockLayoutQualifier))
+    {
+        recover();
+    }
+
     TSymbol *blockNameSymbol = new TInterfaceBlockName(&blockName);
     if (!symbolTable.declare(blockNameSymbol))
     {
@@ -3046,11 +3186,7 @@
 TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
                                                      const TSourceLoc &qualifierTypeLine)
 {
-    TLayoutQualifier qualifier;
-
-    qualifier.location      = -1;
-    qualifier.matrixPacking = EmpUnspecified;
-    qualifier.blockStorage  = EbsUnspecified;
+    TLayoutQualifier qualifier = TLayoutQualifier::create();
 
     if (qualifierType == "shared")
     {
@@ -3087,25 +3223,34 @@
     return qualifier;
 }
 
+void TParseContext::parseLocalSize(const TString &qualifierType,
+                                   const TSourceLoc &qualifierTypeLine,
+                                   int intValue,
+                                   const TSourceLoc &intValueLine,
+                                   const std::string &intValueString,
+                                   size_t index,
+                                   TLocalSize *localSize)
+{
+    layoutSupportedErrorCheck(qualifierTypeLine, qualifierType, 310);
+    if (intValue < 1)
+    {
+        std::string errorMessage = std::string(getLocalSizeString(index)) + " must be positive";
+        error(intValueLine, "out of range:", intValueString.c_str(), errorMessage.c_str());
+        recover();
+    }
+    (*localSize)[index] = intValue;
+}
+
 TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
                                                      const TSourceLoc &qualifierTypeLine,
-                                                     const TString &intValueString,
                                                      int intValue,
                                                      const TSourceLoc &intValueLine)
 {
-    TLayoutQualifier qualifier;
+    TLayoutQualifier qualifier = TLayoutQualifier::create();
 
-    qualifier.location      = -1;
-    qualifier.matrixPacking = EmpUnspecified;
-    qualifier.blockStorage  = EbsUnspecified;
+    std::string intValueString = Str(intValue);
 
-    if (qualifierType != "location")
-    {
-        error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(),
-              "only location may have arguments");
-        recover();
-    }
-    else
+    if (qualifierType == "location")
     {
         // must check that location is non-negative
         if (intValue < 0)
@@ -3119,12 +3264,33 @@
             qualifier.location = intValue;
         }
     }
+    else if (qualifierType == "local_size_x")
+    {
+        parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
+                       &qualifier.localSize);
+    }
+    else if (qualifierType == "local_size_y")
+    {
+        parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 1u,
+                       &qualifier.localSize);
+    }
+    else if (qualifierType == "local_size_z")
+    {
+        parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 2u,
+                       &qualifier.localSize);
+    }
+    else
+    {
+        error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
+        recover();
+    }
 
     return qualifier;
 }
 
 TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier,
-                                                     TLayoutQualifier rightQualifier)
+                                                     TLayoutQualifier rightQualifier,
+                                                     const TSourceLoc &rightQualifierLocation)
 {
     TLayoutQualifier joinedQualifier = leftQualifier;
 
@@ -3141,6 +3307,22 @@
         joinedQualifier.blockStorage = rightQualifier.blockStorage;
     }
 
+    for (size_t i = 0u; i < rightQualifier.localSize.size(); ++i)
+    {
+        if (rightQualifier.localSize[i] != -1)
+        {
+            if (joinedQualifier.localSize[i] != -1 &&
+                joinedQualifier.localSize[i] != rightQualifier.localSize[i])
+            {
+                error(rightQualifierLocation,
+                      "Cannot have multiple different work group size specifiers",
+                      getLocalSizeString(i));
+                recover();
+            }
+            joinedQualifier.localSize[i] = rightQualifier.localSize[i];
+        }
+    }
+
     return joinedQualifier;
 }
 
@@ -3210,6 +3392,11 @@
         recover();
     }
 
+    if (layoutWorkGroupSizeErrorCheck(typeSpecifier.line, typeSpecifier.layoutQualifier))
+    {
+        recover();
+    }
+
     for (unsigned int i = 0; i < fieldList->size(); ++i)
     {
         //
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 6973a2c..ce71488 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -65,8 +65,10 @@
           mUsesFragColor(false),
           mUsesSecondaryOutputs(false),
           mMinProgramTexelOffset(resources.MinProgramTexelOffset),
-          mMaxProgramTexelOffset(resources.MaxProgramTexelOffset)
+          mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
+          mComputeShaderLocalSizeDeclared(false)
     {
+        mComputeShaderLocalSize.fill(-1);
     }
 
     const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
@@ -114,6 +116,9 @@
     void incrSwitchNestingLevel() { ++mSwitchNestingLevel; }
     void decrSwitchNestingLevel() { --mSwitchNestingLevel; }
 
+    bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+    TLocalSize getComputeShaderLocalSize() const;
+
     // This method is guaranteed to succeed, even if no variable with 'name' exists.
     const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
     TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
@@ -149,6 +154,11 @@
     bool extensionErrorCheck(const TSourceLoc &line, const TString&);
     bool singleDeclarationErrorCheck(const TPublicType &publicType, const TSourceLoc &identifierLocation);
     bool layoutLocationErrorCheck(const TSourceLoc &location, const TLayoutQualifier &layoutQualifier);
+    void layoutSupportedErrorCheck(const TSourceLoc &location,
+                                   const TString &layoutQualifierName,
+                                   int versionRequired);
+    bool layoutWorkGroupSizeErrorCheck(const TSourceLoc &location,
+                                       const TLayoutQualifier &layoutQualifier);
     bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *);
     void es3InvariantErrorCheck(const TQualifier qualifier, const TSourceLoc &invariantLocation);
     void es3InputOutputTypeCheck(const TQualifier qualifier,
@@ -279,14 +289,22 @@
                                         TIntermTyped *arrayIndex,
                                         const TSourceLoc& arrayIndexLine);
 
+    void parseLocalSize(const TString &qualifierType,
+                        const TSourceLoc &qualifierTypeLine,
+                        int intValue,
+                        const TSourceLoc &intValueLine,
+                        const std::string &intValueString,
+                        size_t index,
+                        TLocalSize *localSize);
     TLayoutQualifier parseLayoutQualifier(
         const TString &qualifierType, const TSourceLoc &qualifierTypeLine);
     TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
                                           const TSourceLoc &qualifierTypeLine,
-                                          const TString &intValueString,
                                           int intValue,
                                           const TSourceLoc &intValueLine);
-    TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier rightQualifier);
+    TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier,
+                                          TLayoutQualifier rightQualifier,
+                                          const TSourceLoc &rightQualifierLocation);
     TPublicType joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TQualifier interpolationQualifier,
                                             const TSourceLoc &storageLoc, TQualifier storageQualifier);
 
@@ -396,6 +414,10 @@
                                  // gl_Secondary FragColor or both.
     int mMinProgramTexelOffset;
     int mMaxProgramTexelOffset;
+
+    // keep track of local group size declared in layout. It should be declared only once.
+    bool mComputeShaderLocalSizeDeclared;
+    TLocalSize mComputeShaderLocalSize;
 };
 
 int PaParseStrings(
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index c000598..4cbf2e5 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -358,6 +358,17 @@
     return GetShaderVariables<sh::InterfaceBlock>(handle);
 }
 
+std::array<int, 3> ShGetComputeShaderLocalGroupSize(const ShHandle handle)
+{
+    ASSERT(handle);
+
+    TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+    TCompiler *compiler = base->getAsCompiler();
+    ASSERT(compiler);
+
+    return compiler->getComputeShaderLocalSize();
+}
+
 bool ShCheckVariablesWithinPackingLimits(int maxVectors,
                                          const std::vector<sh::ShaderVariable> &variables)
 {
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index f3615a4..c04b13d 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -77,6 +77,13 @@
     // Write array bounds clamping emulation if needed.
     getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
 
+    if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+    {
+        const TLocalSize &localSize = getComputeShaderLocalSize();
+        sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+             << ", local_size_z=" << localSize[2] << ") in;\n";
+    }
+
     // Write translated shader.
     TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
                            getSymbolTable(), shaderVer, precisionEmulation);
diff --git a/src/compiler/translator/TranslatorGLSL.cpp b/src/compiler/translator/TranslatorGLSL.cpp
index d9d6cdc..7174233 100644
--- a/src/compiler/translator/TranslatorGLSL.cpp
+++ b/src/compiler/translator/TranslatorGLSL.cpp
@@ -134,6 +134,13 @@
         }
     }
 
+    if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+    {
+        const TLocalSize &localSize = getComputeShaderLocalSize();
+        sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+             << ", local_size_z=" << localSize[2] << ") in;\n";
+    }
+
     // Write translated shader.
     TOutputGLSL outputGLSL(sink,
                            getArrayIndexClampingStrategy(),
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index 18aea4e..102e373 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -126,6 +126,20 @@
     }  \
 }
 
+#define COMPUTE_ONLY(S, L) {  \
+    if (context->getShaderType() != GL_COMPUTE_SHADER) {  \
+        context->error(L, " supported in compute shaders only ", S);  \
+        context->recover();  \
+    }  \
+}
+
+#define NON_COMPUTE_ONLY(S, L) {  \
+    if (context->getShaderType() != GL_VERTEX_SHADER && context->getShaderType() != GL_FRAGMENT_SHADER) {  \
+        context->error(L, " supported in vertex and fragment shaders only ", S);  \
+        context->recover();  \
+    }  \
+}
+
 #define ES2_ONLY(S, L) {  \
     if (context->getShaderVersion() != 100) {  \
         context->error(L, " supported in GLSL ES 1.00 only ", S);  \
@@ -933,37 +947,35 @@
         $$.qualifier = EvqConst;
     }
     | IN_QUAL {
-        ES3_OR_NEWER("in", @1, "storage qualifier");
         if (context->getShaderType() == GL_FRAGMENT_SHADER)
         {
+            ES3_OR_NEWER("in", @1, "storage qualifier");
             $$.qualifier = EvqFragmentIn;
         }
+        else if (context->getShaderType() == GL_VERTEX_SHADER)
+        {
+            ES3_OR_NEWER("in", @1, "storage qualifier");
+            $$.qualifier = EvqVertexIn;
+        }
         else
         {
-            $$.qualifier = EvqVertexIn;
+            $$.qualifier = EvqComputeIn;
         }
     }
     | OUT_QUAL {
         ES3_OR_NEWER("out", @1, "storage qualifier");
+        NON_COMPUTE_ONLY("out", @1);
         $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut;
     }
     | CENTROID IN_QUAL {
         ES3_OR_NEWER("centroid in", @1, "storage qualifier");
-        if (context->getShaderType() == GL_VERTEX_SHADER)
-        {
-            context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid in' in the vertex shader");
-            context->recover();
-        }
-        $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqCentroidIn : EvqVertexIn;
+        FRAG_ONLY("centroid in", @1);
+        $$.qualifier = EvqCentroidIn;
     }
     | CENTROID OUT_QUAL {
         ES3_OR_NEWER("centroid out", @1, "storage qualifier");
-        if (context->getShaderType() == GL_FRAGMENT_SHADER)
-        {
-            context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid out' in the fragment shader");
-            context->recover();
-        }
-        $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqCentroidOut;
+        VERTEX_ONLY("centroid out", @1);
+        $$.qualifier = EvqCentroidOut;
     }
     | UNIFORM {
         if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "uniform"))
@@ -1018,7 +1030,7 @@
         $$ = $1;
     }
     | layout_qualifier_id_list COMMA layout_qualifier_id {
-        $$ = context->joinLayoutQualifiers($1, $3);
+        $$ = context->joinLayoutQualifiers($1, $3, @3);
     }
     ;
 
@@ -1027,10 +1039,10 @@
         $$ = context->parseLayoutQualifier(*$1.string, @1);
     }
     | IDENTIFIER EQUAL INTCONSTANT {
-        $$ = context->parseLayoutQualifier(*$1.string, @1, *$3.string, $3.i, @3);
+        $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
     }
     | IDENTIFIER EQUAL UINTCONSTANT {
-        $$ = context->parseLayoutQualifier(*$1.string, @1, *$3.string, $3.i, @3);
+        $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
     }
     ;
 
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index 9db956c..101bcd3 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -370,6 +370,20 @@
     }  \
 }
 
+#define COMPUTE_ONLY(S, L) {  \
+    if (context->getShaderType() != GL_COMPUTE_SHADER) {  \
+        context->error(L, " supported in compute shaders only ", S);  \
+        context->recover();  \
+    }  \
+}
+
+#define DRAW_ONLY(S, L) {  \
+    if (context->getShaderType() != GL_VERTEX_SHADER && context->getShaderType() != GL_FRAGMENT_SHADER) {  \
+        context->error(L, " supported in vertex and fragment shaders only ", S);  \
+        context->recover();  \
+    }  \
+}
+
 #define ES2_ONLY(S, L) {  \
     if (context->getShaderVersion() != 100) {  \
         context->error(L, " supported in GLSL ES 1.00 only ", S);  \
@@ -703,34 +717,34 @@
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_uint16 yyrline[] =
 {
-       0,   221,   221,   222,   225,   235,   238,   243,   248,   253,
-     258,   264,   267,   270,   273,   276,   279,   285,   293,   304,
-     308,   316,   319,   325,   329,   336,   342,   351,   359,   365,
-     372,   382,   385,   388,   391,   401,   402,   403,   404,   412,
-     413,   416,   419,   426,   427,   430,   436,   437,   441,   448,
-     449,   452,   455,   458,   464,   465,   468,   474,   475,   482,
-     483,   490,   491,   498,   499,   505,   506,   512,   513,   519,
-     520,   526,   527,   535,   536,   537,   538,   542,   543,   544,
-     548,   552,   556,   560,   567,   570,   576,   584,   592,   595,
-     601,   612,   616,   620,   624,   631,   637,   640,   647,   655,
-     676,   685,   695,   723,   728,   738,   743,   753,   756,   759,
-     762,   768,   775,   778,   782,   786,   791,   796,   803,   807,
-     811,   815,   820,   825,   829,   836,   846,   852,   855,   861,
-     867,   874,   883,   893,   901,   904,   911,   915,   919,   924,
-     932,   935,   946,   950,   959,   968,   976,   986,   998,  1001,
-    1004,  1010,  1017,  1020,  1026,  1029,  1032,  1038,  1041,  1046,
-    1061,  1065,  1069,  1073,  1077,  1081,  1086,  1091,  1096,  1101,
-    1106,  1111,  1116,  1121,  1126,  1131,  1136,  1141,  1146,  1151,
-    1156,  1161,  1166,  1171,  1176,  1181,  1186,  1190,  1194,  1198,
-    1202,  1206,  1210,  1214,  1218,  1222,  1226,  1230,  1234,  1238,
-    1242,  1246,  1255,  1263,  1267,  1280,  1280,  1283,  1283,  1289,
-    1292,  1308,  1311,  1320,  1324,  1330,  1337,  1352,  1356,  1360,
-    1361,  1367,  1368,  1369,  1370,  1371,  1372,  1373,  1377,  1378,
-    1378,  1378,  1388,  1389,  1393,  1393,  1394,  1394,  1399,  1402,
-    1412,  1415,  1421,  1422,  1426,  1434,  1438,  1445,  1445,  1452,
-    1455,  1462,  1467,  1482,  1482,  1487,  1487,  1494,  1494,  1502,
-    1505,  1511,  1514,  1520,  1524,  1531,  1534,  1537,  1540,  1543,
-    1552,  1556,  1563,  1566,  1572,  1572
+       0,   235,   235,   236,   239,   249,   252,   257,   262,   267,
+     272,   278,   281,   284,   287,   290,   293,   299,   307,   318,
+     322,   330,   333,   339,   343,   350,   356,   365,   373,   379,
+     386,   396,   399,   402,   405,   415,   416,   417,   418,   426,
+     427,   430,   433,   440,   441,   444,   450,   451,   455,   462,
+     463,   466,   469,   472,   478,   479,   482,   488,   489,   496,
+     497,   504,   505,   512,   513,   519,   520,   526,   527,   533,
+     534,   540,   541,   549,   550,   551,   552,   556,   557,   558,
+     562,   566,   570,   574,   581,   584,   590,   598,   606,   609,
+     615,   626,   630,   634,   638,   645,   651,   654,   661,   669,
+     690,   699,   709,   737,   742,   752,   757,   767,   770,   773,
+     776,   782,   789,   792,   796,   800,   805,   810,   817,   821,
+     825,   829,   834,   839,   843,   850,   860,   866,   869,   875,
+     881,   888,   897,   907,   915,   918,   925,   929,   933,   938,
+     946,   949,   965,   970,   975,   980,   988,   998,  1010,  1013,
+    1016,  1022,  1029,  1032,  1038,  1041,  1044,  1050,  1053,  1058,
+    1073,  1077,  1081,  1085,  1089,  1093,  1098,  1103,  1108,  1113,
+    1118,  1123,  1128,  1133,  1138,  1143,  1148,  1153,  1158,  1163,
+    1168,  1173,  1178,  1183,  1188,  1193,  1198,  1202,  1206,  1210,
+    1214,  1218,  1222,  1226,  1230,  1234,  1238,  1242,  1246,  1250,
+    1254,  1258,  1267,  1275,  1279,  1292,  1292,  1295,  1295,  1301,
+    1304,  1320,  1323,  1332,  1336,  1342,  1349,  1364,  1368,  1372,
+    1373,  1379,  1380,  1381,  1382,  1383,  1384,  1385,  1389,  1390,
+    1390,  1390,  1400,  1401,  1405,  1405,  1406,  1406,  1411,  1414,
+    1424,  1427,  1433,  1434,  1438,  1446,  1450,  1457,  1457,  1464,
+    1467,  1474,  1479,  1494,  1494,  1499,  1499,  1506,  1506,  1514,
+    1517,  1523,  1526,  1532,  1536,  1543,  1546,  1549,  1552,  1555,
+    1564,  1568,  1575,  1578,  1584,  1584
 };
 #endif
 
@@ -3607,14 +3621,19 @@
   case 141:
 
     {
-        ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
         if (context->getShaderType() == GL_FRAGMENT_SHADER)
         {
+            ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
             (yyval.interm.type).qualifier = EvqFragmentIn;
         }
+        else if (context->getShaderType() == GL_VERTEX_SHADER)
+        {
+            ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
+            (yyval.interm.type).qualifier = EvqVertexIn;
+        }
         else
         {
-            (yyval.interm.type).qualifier = EvqVertexIn;
+            (yyval.interm.type).qualifier = EvqComputeIn;
         }
     }
 
@@ -3624,6 +3643,7 @@
 
     {
         ES3_OR_NEWER("out", (yylsp[0]), "storage qualifier");
+        DRAW_ONLY("out", (yylsp[0]));
         (yyval.interm.type).qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut;
     }
 
@@ -3633,12 +3653,8 @@
 
     {
         ES3_OR_NEWER("centroid in", (yylsp[-1]), "storage qualifier");
-        if (context->getShaderType() == GL_VERTEX_SHADER)
-        {
-            context->error((yylsp[-1]), "invalid storage qualifier", "it is an error to use 'centroid in' in the vertex shader");
-            context->recover();
-        }
-        (yyval.interm.type).qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqCentroidIn : EvqVertexIn;
+        FRAG_ONLY("centroid in", (yylsp[-1]));
+        (yyval.interm.type).qualifier = EvqCentroidIn;
     }
 
     break;
@@ -3647,12 +3663,8 @@
 
     {
         ES3_OR_NEWER("centroid out", (yylsp[-1]), "storage qualifier");
-        if (context->getShaderType() == GL_FRAGMENT_SHADER)
-        {
-            context->error((yylsp[-1]), "invalid storage qualifier", "it is an error to use 'centroid out' in the fragment shader");
-            context->recover();
-        }
-        (yyval.interm.type).qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqCentroidOut;
+        VERTEX_ONLY("centroid out", (yylsp[-1]));
+        (yyval.interm.type).qualifier = EvqCentroidOut;
     }
 
     break;
@@ -3740,7 +3752,7 @@
   case 153:
 
     {
-        (yyval.interm.layoutQualifier) = context->joinLayoutQualifiers((yyvsp[-2].interm.layoutQualifier), (yyvsp[0].interm.layoutQualifier));
+        (yyval.interm.layoutQualifier) = context->joinLayoutQualifiers((yyvsp[-2].interm.layoutQualifier), (yyvsp[0].interm.layoutQualifier), (yylsp[0]));
     }
 
     break;
@@ -3756,7 +3768,7 @@
   case 155:
 
     {
-        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), *(yyvsp[0].lex).string, (yyvsp[0].lex).i, (yylsp[0]));
+        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
     }
 
     break;
@@ -3764,7 +3776,7 @@
   case 156:
 
     {
-        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), *(yyvsp[0].lex).string, (yyvsp[0].lex).i, (yylsp[0]));
+        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
     }
 
     break;