From 0f5e3ad23cb69c46bfbeff9c61047e6ceccfa62e Mon Sep 17 00:00:00 2001
From: John Kessenich <cepheus@frii.com>
Date: Sun, 29 May 2016 18:24:31 -0600
Subject: [PATCH] Fix issue #313: Catch internal attempts to modify built-in
 symbols that don't exist.

Also beefed up support for running compute shaders is #version 420, but this
work is only partially done.
---
 Test/420.comp                              |  7 +++++
 Test/baseResults/420.comp.out              | 32 ++++++++++++++++++++++
 glslang/MachineIndependent/Initialize.cpp  | 11 +++++++-
 glslang/MachineIndependent/ParseHelper.cpp | 20 ++++++++++++--
 glslang/MachineIndependent/ShaderLang.cpp  |  6 ++--
 gtests/AST.FromFile.cpp                    |  1 +
 6 files changed, 70 insertions(+), 7 deletions(-)
 create mode 100755 Test/420.comp
 create mode 100755 Test/baseResults/420.comp.out

diff --git a/Test/420.comp b/Test/420.comp
new file mode 100755
index 000000000..4e6885bea
--- /dev/null
+++ b/Test/420.comp
@@ -0,0 +1,7 @@
+#version 420
+
+layout(local_size_x = 2) in;  // ERROR, no compute
+
+#extension GL_ARB_compute_shader : enable
+
+layout(local_size_x = 2) in;
diff --git a/Test/baseResults/420.comp.out b/Test/baseResults/420.comp.out
new file mode 100755
index 000000000..c21df0769
--- /dev/null
+++ b/Test/baseResults/420.comp.out
@@ -0,0 +1,32 @@
+420.comp
+Warning, version 420 is not yet complete; most version-specific features are present, but some are missing.
+ERROR: 0:3: 'gl_WorkgroupSize' : not supported for this version or the enabled extensions 
+WARNING: 0:5: '#extension' : extension is only partially supported: GL_ARB_compute_shader
+ERROR: 1 compilation errors.  No code generated.
+
+
+Shader version: 420
+Requested GL_ARB_compute_shader
+local_size = (2, 1, 1)
+ERROR: node is still EOpNull!
+0:?   Linker Objects
+0:?     'gl_WorkGroupSize' (const 3-component vector of uint WorkGroupSize)
+0:?       2 (const uint)
+0:?       1 (const uint)
+0:?       1 (const uint)
+
+
+Linked compute stage:
+
+ERROR: Linking compute stage: Missing entry point: Each stage requires one "void main()" entry point
+
+Shader version: 420
+Requested GL_ARB_compute_shader
+local_size = (2, 1, 1)
+ERROR: node is still EOpNull!
+0:?   Linker Objects
+0:?     'gl_WorkGroupSize' (const 3-component vector of uint WorkGroupSize)
+0:?       2 (const uint)
+0:?       1 (const uint)
+0:?       1 (const uint)
+
diff --git a/glslang/MachineIndependent/Initialize.cpp b/glslang/MachineIndependent/Initialize.cpp
index 034962a27..c4ffdf204 100644
--- a/glslang/MachineIndependent/Initialize.cpp
+++ b/glslang/MachineIndependent/Initialize.cpp
@@ -1871,7 +1871,7 @@ void TBuiltIns::initialize(int version, EProfile profile, int spv, int vulkan)
     //
     //============================================================================
 
-    if ((profile != EEsProfile && version >= 430) ||
+    if ((profile != EEsProfile && version >= 420) ||
         (profile == EEsProfile && version >= 310)) {
         stageBuiltins[EShLangCompute].append(
             "in    highp uvec3 gl_NumWorkGroups;"
@@ -3896,6 +3896,15 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, int spv, int vul
         BuiltInVariable("gl_LocalInvocationID",     EbvLocalInvocationId,    symbolTable);
         BuiltInVariable("gl_GlobalInvocationID",    EbvGlobalInvocationId,   symbolTable);
         BuiltInVariable("gl_LocalInvocationIndex",  EbvLocalInvocationIndex, symbolTable);
+
+        if (profile != EEsProfile && version < 430) {
+            symbolTable.setVariableExtensions("gl_NumWorkGroups",        1, &E_GL_ARB_compute_shader);
+            symbolTable.setVariableExtensions("gl_WorkGroupSize",        1, &E_GL_ARB_compute_shader);
+            symbolTable.setVariableExtensions("gl_WorkGroupID",          1, &E_GL_ARB_compute_shader);
+            symbolTable.setVariableExtensions("gl_LocalInvocationID",    1, &E_GL_ARB_compute_shader);
+            symbolTable.setVariableExtensions("gl_GlobalInvocationID",   1, &E_GL_ARB_compute_shader);
+            symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_ARB_compute_shader);
+        }
         break;
 
     default:
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 15d0dae31..39344cf5d 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -622,10 +622,20 @@ void TParseContext::makeEditable(TSymbol*& symbol)
     intermediate.addSymbolLinkageNode(linkage, *symbol);
 }
 
+// Return a writable version of the variable 'name'.
+//
+// Return nullptr if 'name' is not found.  This should mean
+// something is seriously wrong (e.g., compiler asking self for
+// built-in that doesn't exist).
 TVariable* TParseContext::getEditableVariable(const char* name)
 {
     bool builtIn;
     TSymbol* symbol = symbolTable.find(name, &builtIn);
+    
+    assert(symbol != nullptr);
+    if (symbol == nullptr)
+        return nullptr;
+
     if (builtIn)
         makeEditable(symbol);
 
@@ -3844,7 +3854,7 @@ void TParseContext::finalErrorCheck()
         break;
     case EShLangCompute:
         if (profile != EEsProfile && version < 430)
-            requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_compute_shader, "tessellation shaders");
+            requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_compute_shader, "compute shaders");
         break;
     default:
         break;
@@ -4222,6 +4232,8 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
 
     case EShLangCompute:
         if (id.compare(0, 11, "local_size_") == 0) {
+            profileRequires(loc, EEsProfile, 310, 0, "gl_WorkgroupSize");
+            profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_compute_shader, "gl_WorkgroupSize");
             if (id == "local_size_x") {
                 publicType.shaderQualifiers.localSize[0] = value;
                 return;
@@ -5967,7 +5979,8 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con
 
                     // Fix the existing constant gl_WorkGroupSize with this new information.
                     TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
-                    workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
+                    if (workGroupSize != nullptr)
+                        workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
                 }
             } else
                 error(loc, "can only apply to 'in'", "local_size", "");
@@ -5980,7 +5993,8 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con
                 error(loc, "can only apply to 'in'", "local_size id", "");
             // Set the workgroup built-in variable as a specialization constant
             TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
-            workGroupSize->getWritableType().getQualifier().specConstant = true;
+            if (workGroupSize != nullptr)
+                workGroupSize->getWritableType().getQualifier().specConstant = true;
         }
     }
     if (publicType.shaderQualifiers.earlyFragmentTests) {
diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp
index 1446298ce..1abb55357 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -235,10 +235,10 @@ bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable,  TS
         InitializeStageSymbolTable(*builtInParseables, version, profile, spv, vulkan, EShLangGeometry, infoSink, commonTable, symbolTables);
 
     // check for compute
-    if ((profile != EEsProfile && version >= 430) ||
+    if ((profile != EEsProfile && version >= 420) ||
         (profile == EEsProfile && version >= 310))
         InitializeStageSymbolTable(*builtInParseables, version, profile, spv, vulkan, EShLangCompute, infoSink, commonTable, symbolTables);
-    
+
     return true;
 }
 
@@ -417,7 +417,7 @@ bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNo
             (profile != EEsProfile && version < 420)) {
             correct = false;
             infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above");
-            version = profile == EEsProfile ? 310 : 430; // 420 supports the extension, correction is to 430 which does not
+            version = profile == EEsProfile ? 310 : 420;
         }
         break;
     default:
diff --git a/gtests/AST.FromFile.cpp b/gtests/AST.FromFile.cpp
index 3f4819a30..08ec919b0 100644
--- a/gtests/AST.FromFile.cpp
+++ b/gtests/AST.FromFile.cpp
@@ -113,6 +113,7 @@ INSTANTIATE_TEST_CASE_P(
         "110scope.vert",
         "300scope.vert",
         "400.frag",
+        "420.comp",
         "420.frag",
         "420.vert",
         "420.geom",
-- 
GitLab