Simplify DeferGlobalInitializers

It doesn't need to use a traverser, it's simpler to just iterate over
all statements in the global scope.

BUG=angleproject:1966
TEST=angle_unittests, WebGL conformance tests

Change-Id: I11200f72842db86be2bcdd11934262da183cc3b4
Reviewed-on: https://chromium-review.googlesource.com/504727
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/DeferGlobalInitializers.cpp b/src/compiler/translator/DeferGlobalInitializers.cpp
index c1585a5..cdc8a37 100644
--- a/src/compiler/translator/DeferGlobalInitializers.cpp
+++ b/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -22,105 +22,88 @@
 namespace
 {
 
-class DeferGlobalInitializersTraverser : public TIntermTraverser
+void GetDeferredInitializers(TIntermDeclaration *declaration,
+                             TIntermSequence *deferredInitializersOut)
 {
-  public:
-    DeferGlobalInitializersTraverser();
-
-    bool visitBinary(Visit visit, TIntermBinary *node) override;
-
-    void insertInitFunction(TIntermBlock *root);
-
-  private:
-    TIntermSequence mDeferredInitializers;
-};
-
-DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
-    : TIntermTraverser(true, false, false)
-{
-}
-
-bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
-{
-    if (node->getOp() == EOpInitialize)
+    // We iterate with an index instead of using an iterator since we're replacing the children of
+    // declaration inside the loop.
+    for (size_t i = 0; i < declaration->getSequence()->size(); ++i)
     {
-        TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
-        ASSERT(symbolNode);
-        TIntermTyped *expression = node->getRight();
-
-        if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
-                               (expression->getAsConstantUnion() == nullptr &&
-                                !expression->isConstructorWithOnlyConstantUnionParameters())))
+        TIntermNode *declarator = declaration->getSequence()->at(i);
+        TIntermBinary *init     = declarator->getAsBinaryNode();
+        if (init)
         {
-            // For variables which are not constant, defer their real initialization until
-            // after we initialize uniforms.
-            // Deferral is done also in any cases where the variable has not been constant folded,
-            // since otherwise there's a chance that HLSL output will generate extra statements
-            // from the initializer expression.
-            TIntermBinary *deferredInit =
-                new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
-            mDeferredInitializers.push_back(deferredInit);
+            TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
+            ASSERT(symbolNode);
+            TIntermTyped *expression = init->getRight();
 
-            // Change const global to a regular global if its initialization is deferred.
-            // This can happen if ANGLE has not been able to fold the constant expression used
-            // as an initializer.
-            ASSERT(symbolNode->getQualifier() == EvqConst ||
-                   symbolNode->getQualifier() == EvqGlobal);
-            if (symbolNode->getQualifier() == EvqConst)
+            if ((expression->getQualifier() != EvqConst ||
+                 (expression->getAsConstantUnion() == nullptr &&
+                  !expression->isConstructorWithOnlyConstantUnionParameters())))
             {
-                // All of the siblings in the same declaration need to have consistent qualifiers.
-                auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
-                for (TIntermNode *siblingNode : *siblings)
+                // For variables which are not constant, defer their real initialization until
+                // after we initialize uniforms.
+                // Deferral is done also in any cases where the variable has not been constant
+                // folded, since otherwise there's a chance that HLSL output will generate extra
+                // statements from the initializer expression.
+                TIntermBinary *deferredInit =
+                    new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
+                deferredInitializersOut->push_back(deferredInit);
+
+                // Change const global to a regular global if its initialization is deferred.
+                // This can happen if ANGLE has not been able to fold the constant expression used
+                // as an initializer.
+                ASSERT(symbolNode->getQualifier() == EvqConst ||
+                       symbolNode->getQualifier() == EvqGlobal);
+                if (symbolNode->getQualifier() == EvqConst)
                 {
-                    TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
-                    if (siblingBinary)
+                    // All of the siblings in the same declaration need to have consistent
+                    // qualifiers.
+                    auto *siblings = declaration->getSequence();
+                    for (TIntermNode *siblingNode : *siblings)
                     {
-                        ASSERT(siblingBinary->getOp() == EOpInitialize);
-                        siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
+                        TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
+                        if (siblingBinary)
+                        {
+                            ASSERT(siblingBinary->getOp() == EOpInitialize);
+                            siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
+                        }
+                        siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
                     }
-                    siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
+                    // This node is one of the siblings.
+                    ASSERT(symbolNode->getQualifier() == EvqGlobal);
                 }
-                // This node is one of the siblings.
-                ASSERT(symbolNode->getQualifier() == EvqGlobal);
+                // Remove the initializer from the global scope and just declare the global instead.
+                declaration->replaceChildNode(init, symbolNode);
             }
-            // Remove the initializer from the global scope and just declare the global instead.
-            queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
         }
     }
-    return false;
 }
 
-void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
+void InsertInitFunction(TIntermBlock *root, TIntermSequence *deferredInitializers)
 {
-    if (mDeferredInitializers.empty())
-    {
-        return;
-    }
     TSymbolUniqueId initFunctionId;
-
-    const char *functionName = "initializeDeferredGlobals";
+    const char *functionName = "initializeGlobals";
 
     // Add function prototype to the beginning of the shader
     TIntermFunctionPrototype *functionPrototypeNode =
-        CreateInternalFunctionPrototypeNode(TType(EbtVoid), functionName, initFunctionId);
+        TIntermTraverser::CreateInternalFunctionPrototypeNode(TType(EbtVoid), functionName,
+                                                              initFunctionId);
     root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
 
     // Add function definition to the end of the shader
     TIntermBlock *functionBodyNode = new TIntermBlock();
-    TIntermSequence *functionBody  = functionBodyNode->getSequence();
-    for (const auto &deferredInit : mDeferredInitializers)
-    {
-        functionBody->push_back(deferredInit);
-    }
-    TIntermFunctionDefinition *functionDefinition = CreateInternalFunctionDefinitionNode(
-        TType(EbtVoid), functionName, functionBodyNode, initFunctionId);
+    functionBodyNode->getSequence()->swap(*deferredInitializers);
+    TIntermFunctionDefinition *functionDefinition =
+        TIntermTraverser::CreateInternalFunctionDefinitionNode(TType(EbtVoid), functionName,
+                                                               functionBodyNode, initFunctionId);
     root->getSequence()->push_back(functionDefinition);
 
     // Insert call into main function
     TIntermFunctionDefinition *main = FindMain(root);
     ASSERT(main != nullptr);
-    TIntermAggregate *functionCallNode =
-        CreateInternalFunctionCallNode(TType(EbtVoid), functionName, initFunctionId, nullptr);
+    TIntermAggregate *functionCallNode = TIntermTraverser::CreateInternalFunctionCallNode(
+        TType(EbtVoid), functionName, initFunctionId, nullptr);
 
     TIntermBlock *mainBody = main->getBody();
     ASSERT(mainBody != nullptr);
@@ -131,14 +114,24 @@
 
 void DeferGlobalInitializers(TIntermBlock *root)
 {
-    DeferGlobalInitializersTraverser traverser;
-    root->traverse(&traverser);
+    TIntermSequence *deferredInitializers = new TIntermSequence();
 
-    // Replace the initializers of the global variables.
-    traverser.updateTree();
+    // Loop over all global statements and process the declarations. This is simpler than using a
+    // traverser.
+    for (TIntermNode *declaration : *root->getSequence())
+    {
+        TIntermDeclaration *asVariableDeclaration = declaration->getAsDeclarationNode();
+        if (asVariableDeclaration)
+        {
+            GetDeferredInitializers(asVariableDeclaration, deferredInitializers);
+        }
+    }
 
     // Add the function with initialization and the call to that.
-    traverser.insertInitFunction(root);
+    if (!deferredInitializers->empty())
+    {
+        InsertInitFunction(root, deferredInitializers);
+    }
 }
 
 }  // namespace sh