Remove unreferenced variables from the AST

Unreferenced local and global variables are now pruned from the AST.
They will be removed unless their initializer has side effects.

The CollectVariables step needs to be run after the pruning, as the
pruning may affect which interface variables are statically used.

It's also good to gather built-ins that need to be emulated after the
pruning, so unnecessary built-in emulation functions are not added to
the translator output.

This will help handle some dEQP tests for arrays of arrays that have
extremely large local arrays that are only used in an array length
query. By constant folding the length and pruning unused variables we
will avoid adding a large amount of array initialization code to the
generated shaders.

BUG=angleproject:2166
TEST=angle_unittests, angle_end2end_tests

Change-Id: Ic918bfe8f16460bcd6101d73a7a674145f5aeecd
Reviewed-on: https://chromium-review.googlesource.com/766434
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 4784171..0823ead 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -32,6 +32,7 @@
 #include "compiler/translator/RemoveInvariantDeclaration.h"
 #include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h"
 #include "compiler/translator/RemovePow.h"
+#include "compiler/translator/RemoveUnreferencedVariables.h"
 #include "compiler/translator/RewriteDoWhile.h"
 #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
 #include "compiler/translator/SeparateDeclarations.h"
@@ -468,13 +469,6 @@
         return false;
     }
 
-    // Built-in function emulation needs to happen after validateLimitations pass.
-    // TODO(jmadill): Remove global pool allocator.
-    GetGlobalPoolAllocator()->lock();
-    initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
-    GetGlobalPoolAllocator()->unlock();
-    builtInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
-
     // Clamping uniform array bounds needs to happen after validateLimitations pass.
     if (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)
     {
@@ -508,56 +502,6 @@
         RemovePow(root);
     }
 
-    if (shouldCollectVariables(compileOptions))
-    {
-        ASSERT(!variablesCollected);
-        CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
-                         &outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
-                         hashFunction, &symbolTable, shaderVersion, shaderType, extensionBehavior);
-        collectInterfaceBlocks();
-        variablesCollected = true;
-        if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
-        {
-            useAllMembersInUnusedStandardAndSharedBlocks(root);
-        }
-        if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
-        {
-            // Returns true if, after applying the packing rules in the GLSL ES 1.00.17 spec
-            // Appendix A, section 7, the shader does not use too many uniforms.
-            if (!CheckVariablesInPackingLimits(maxUniformVectors, uniforms))
-            {
-                mDiagnostics.globalError("too many uniforms");
-                return false;
-            }
-        }
-        if (compileOptions & SH_INIT_OUTPUT_VARIABLES)
-        {
-            initializeOutputVariables(root);
-        }
-    }
-
-    // gl_Position is always written in compatibility output mode.
-    // It may have been already initialized among other output variables, in that case we don't
-    // need to initialize it twice.
-    if (shaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
-        ((compileOptions & SH_INIT_GL_POSITION) || (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
-    {
-        initializeGLPosition(root);
-        mGLPositionInitialized = true;
-    }
-
-    // Removing invariant declarations must be done after collecting variables.
-    // Otherwise, built-in invariant declarations don't apply.
-    if (RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
-    {
-        sh::RemoveInvariantDeclaration(root);
-    }
-
-    if (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS)
-    {
-        ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh, &symbolTable);
-    }
-
     if (compileOptions & SH_REGENERATE_STRUCT_NAMES)
     {
         RegenerateStructNames gen(&symbolTable, shaderVersion);
@@ -589,6 +533,65 @@
 
     RemoveArrayLengthMethod(root);
 
+    RemoveUnreferencedVariables(root, &symbolTable);
+
+    // Built-in function emulation needs to happen after validateLimitations pass.
+    // TODO(jmadill): Remove global pool allocator.
+    GetGlobalPoolAllocator()->lock();
+    initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
+    GetGlobalPoolAllocator()->unlock();
+    builtInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
+
+    if (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS)
+    {
+        ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh, &symbolTable);
+    }
+
+    if (shouldCollectVariables(compileOptions))
+    {
+        ASSERT(!variablesCollected);
+        CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
+                         &outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
+                         hashFunction, &symbolTable, shaderVersion, shaderType, extensionBehavior);
+        collectInterfaceBlocks();
+        variablesCollected = true;
+        if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
+        {
+            useAllMembersInUnusedStandardAndSharedBlocks(root);
+        }
+        if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
+        {
+            // Returns true if, after applying the packing rules in the GLSL ES 1.00.17 spec
+            // Appendix A, section 7, the shader does not use too many uniforms.
+            if (!CheckVariablesInPackingLimits(maxUniformVectors, uniforms))
+            {
+                mDiagnostics.globalError("too many uniforms");
+                return false;
+            }
+        }
+        if (compileOptions & SH_INIT_OUTPUT_VARIABLES)
+        {
+            initializeOutputVariables(root);
+        }
+    }
+
+    // Removing invariant declarations must be done after collecting variables.
+    // Otherwise, built-in invariant declarations don't apply.
+    if (RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
+    {
+        RemoveInvariantDeclaration(root);
+    }
+
+    // gl_Position is always written in compatibility output mode.
+    // It may have been already initialized among other output variables, in that case we don't
+    // need to initialize it twice.
+    if (shaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
+        ((compileOptions & SH_INIT_GL_POSITION) || (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
+    {
+        initializeGLPosition(root);
+        mGLPositionInitialized = true;
+    }
+
     // DeferGlobalInitializers needs to be run before other AST transformations that generate new
     // statements from expressions. But it's fine to run DeferGlobalInitializers after the above
     // SplitSequenceOperator and RemoveArrayLengthMethod since they only have an effect on the AST
@@ -598,7 +601,6 @@
     bool canUseLoopsToInitialize = !(compileOptions & SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES);
     DeferGlobalInitializers(root, initializeLocalsAndGlobals, canUseLoopsToInitialize, &symbolTable);
 
-
     if (initializeLocalsAndGlobals)
     {
         // Initialize uninitialized local variables.