GLSL: Fix initializing globals declared after main()

Initialize globals in a separate function instead of a block in the
beginning of main(). This way it works also for globals declared after
main().

BUG=chromium:764036
TEST=angle_end2end_tests

Change-Id: I2fcbb97d046589301287757dc3dde5471172a3f6
Reviewed-on: https://chromium-review.googlesource.com/663158
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index c7da64f..44578c1 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -523,7 +523,7 @@
 
         if (success)
         {
-            DeferGlobalInitializers(root, needToInitializeGlobalsInAST());
+            DeferGlobalInitializers(root, needToInitializeGlobalsInAST(), &symbolTable);
         }
 
         if (success && (compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) && getOutputType())
diff --git a/src/compiler/translator/DeferGlobalInitializers.cpp b/src/compiler/translator/DeferGlobalInitializers.cpp
index 67ccdf6..9a0073e 100644
--- a/src/compiler/translator/DeferGlobalInitializers.cpp
+++ b/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -3,11 +3,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// DeferGlobalInitializers is an AST traverser that moves global initializers into a block in the
-// beginning of main(). This enables initialization of globals with uniforms or non-constant
-// globals, as allowed by the WebGL spec. Some initializers referencing non-constants may need to be
-// unfolded into if statements in HLSL - this kind of steps should be done after
-// DeferGlobalInitializers is run.
+// DeferGlobalInitializers is an AST traverser that moves global initializers into a separate
+// function that is called in the beginning of main(). This enables initialization of globals with
+// uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing
+// non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be
+// done after DeferGlobalInitializers is run. Note that it's important that the function definition
+// is at the end of the shader, as some globals may be declared after main().
 //
 // It can also initialize all uninitialized globals.
 //
@@ -17,6 +18,7 @@
 #include "compiler/translator/FindMain.h"
 #include "compiler/translator/InitializeVariables.h"
 #include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
 #include "compiler/translator/SymbolTable.h"
 
 namespace sh
@@ -100,19 +102,36 @@
     }
 }
 
-void InsertInitCodeToMain(TIntermBlock *root, TIntermSequence *deferredInitializers)
+void InsertInitCallToMain(TIntermBlock *root,
+                          TIntermSequence *deferredInitializers,
+                          TSymbolTable *symbolTable)
 {
-    // Insert init code as a block to the beginning of the main() function.
     TIntermBlock *initGlobalsBlock = new TIntermBlock();
     initGlobalsBlock->getSequence()->swap(*deferredInitializers);
 
+    TSymbolUniqueId initGlobalsFunctionId(symbolTable);
+
+    const char *kInitGlobalsFunctionName = "initGlobals";
+
+    TIntermFunctionPrototype *initGlobalsFunctionPrototype =
+        CreateInternalFunctionPrototypeNode(TType(), kInitGlobalsFunctionName, initGlobalsFunctionId);
+    root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype);
+    TIntermFunctionDefinition *initGlobalsFunctionDefinition = CreateInternalFunctionDefinitionNode(
+        TType(), kInitGlobalsFunctionName, initGlobalsBlock, initGlobalsFunctionId);
+    root->appendStatement(initGlobalsFunctionDefinition);
+
+    TIntermAggregate *initGlobalsCall = CreateInternalFunctionCallNode(
+        TType(), kInitGlobalsFunctionName, initGlobalsFunctionId, new TIntermSequence());
+
     TIntermBlock *mainBody = FindMainBody(root);
-    mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsBlock);
+    mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall);
 }
 
 }  // namespace
 
-void DeferGlobalInitializers(TIntermBlock *root, bool initializeUninitializedGlobals)
+void DeferGlobalInitializers(TIntermBlock *root,
+                             bool initializeUninitializedGlobals,
+                             TSymbolTable *symbolTable)
 {
     TIntermSequence *deferredInitializers = new TIntermSequence();
 
@@ -131,7 +150,7 @@
     // Add the function with initialization and the call to that.
     if (!deferredInitializers->empty())
     {
-        InsertInitCodeToMain(root, deferredInitializers);
+        InsertInitCallToMain(root, deferredInitializers, symbolTable);
     }
 }
 
diff --git a/src/compiler/translator/DeferGlobalInitializers.h b/src/compiler/translator/DeferGlobalInitializers.h
index 73779cc..18ec66e 100644
--- a/src/compiler/translator/DeferGlobalInitializers.h
+++ b/src/compiler/translator/DeferGlobalInitializers.h
@@ -3,11 +3,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// DeferGlobalInitializers is an AST traverser that moves global initializers into a block in the
-// beginning of main(). This enables initialization of globals with uniforms or non-constant
-// globals, as allowed by the WebGL spec. Some initializers referencing non-constants may need to be
-// unfolded into if statements in HLSL - this kind of steps should be done after
-// DeferGlobalInitializers is run.
+// DeferGlobalInitializers is an AST traverser that moves global initializers into a separate
+// function that is called in the beginning of main(). This enables initialization of globals with
+// uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing
+// non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be
+// done after DeferGlobalInitializers is run. Note that it's important that the function definition
+// is at the end of the shader, as some globals may be declared after main().
 //
 // It can also initialize all uninitialized globals.
 //
@@ -15,11 +16,16 @@
 #ifndef COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
 #define COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
 
-class TIntermBlock;
-
 namespace sh
 {
-void DeferGlobalInitializers(TIntermBlock *root, bool initializeUninitializedGlobals);
+
+class TIntermBlock;
+class TSymbolTable;
+
+void DeferGlobalInitializers(TIntermBlock *root,
+                             bool initializeUninitializedGlobals,
+                             TSymbolTable *symbolTable);
+
 }  // namespace sh
 
 #endif  // COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_