Put each array declarator into a separate declaration in HLSL output

Since HLSL doesn't support arrays as l-values, HLSL output needs to split
declarators that initialize arrays to variable declaration and assignment
implemented via a function call. To prepare for this, it is necessary that each
declarator has its own declaration.

BUG=angleproject:941
TEST=angle_end2end_tests, WebGL conformance tests

Change-Id: I43dee487578561c01dbde90c2f55a93dda2f057a
Reviewed-on: https://chromium-review.googlesource.com/266001
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index f335bfd..a8018a4 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -195,6 +195,20 @@
     return false;
 }
 
+bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements)
+{
+    for (auto it = mSequence.begin(); it < mSequence.end(); ++it)
+    {
+        if (*it == original)
+        {
+            it = mSequence.erase(it);
+            it = mSequence.insert(it, replacements.begin(), replacements.end());
+            return true;
+        }
+    }
+    return false;
+}
+
 void TIntermAggregate::setPrecisionFromChildren()
 {
     if (getBasicType() == EbtBool)
@@ -1497,4 +1511,12 @@
             }
         }
     }
+    for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii)
+    {
+        const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii];
+        ASSERT(replacement.parent);
+        bool replaced = replacement.parent->replaceChildNodeWithMultiple(
+            replacement.original, replacement.replacements);
+        ASSERT(replaced);
+    }
 }
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 57d772c..0bacd3d 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -422,7 +422,7 @@
     virtual void traverse(TIntermTraverser *);
     virtual bool replaceChildNode(
         TIntermNode *original, TIntermNode *replacement);
-
+    bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements);
     // Conservatively assume function calls and other aggregate operators have side-effects
     virtual bool hasSideEffects() const { return true; }
 
@@ -625,7 +625,7 @@
     const bool rightToLeft;
 
     // If traversers need to replace nodes, they can add the replacements in
-    // mReplacements during traversal and the user of the traverser should call
+    // mReplacements/mMultiReplacements during traversal and the user of the traverser should call
     // this function after traversal to perform them.
     void updateTree();
 
@@ -636,6 +636,7 @@
     // All the nodes from root to the current node's parent during traversing.
     TVector<TIntermNode *> mPath;
 
+    // To replace a single node with another on the parent node
     struct NodeUpdateEntry
     {
         NodeUpdateEntry(TIntermNode *_parent,
@@ -653,9 +654,26 @@
         bool originalBecomesChildOfReplacement;
     };
 
+    // To replace a single node with multiple nodes on the parent aggregate node
+    struct NodeReplaceWithMultipleEntry
+    {
+        NodeReplaceWithMultipleEntry(TIntermAggregate *_parent, TIntermNode *_original, TIntermSequence _replacements)
+            : parent(_parent),
+              original(_original),
+              replacements(_replacements)
+        {
+        }
+
+        TIntermAggregate *parent;
+        TIntermNode *original;
+        TIntermSequence replacements;
+    };
+
     // During traversing, save all the changes that need to happen into
-    // mReplacements, then do them by calling updateTree().
+    // mReplacements/mMultiReplacements, then do them by calling updateTree().
+    // Multi replacements are processed after single replacements.
     std::vector<NodeUpdateEntry> mReplacements;
+    std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements;
 };
 
 //
diff --git a/src/compiler/translator/SeparateDeclarations.cpp b/src/compiler/translator/SeparateDeclarations.cpp
new file mode 100644
index 0000000..7ea1ce0
--- /dev/null
+++ b/src/compiler/translator/SeparateDeclarations.cpp
@@ -0,0 +1,86 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateArrayDeclarations function processes declarations that contain array declarators. Each declarator in
+// such declarations gets its own declaration.
+// This is useful as an intermediate step when initialization needs to be separated from declaration.
+// Example:
+//     int a[1] = int[1](1), b[1] = int[1](2);
+// gets transformed when run through this class into the AST equivalent of:
+//     int a[1] = int[1](1);
+//     int b[1] = int[1](2);
+
+#include "compiler/translator/SeparateDeclarations.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace
+{
+
+class SeparateDeclarations : private TIntermTraverser
+{
+  public:
+    static void apply(TIntermNode *root);
+  private:
+    SeparateDeclarations();
+    bool visitAggregate(Visit, TIntermAggregate *node) override;
+};
+
+void SeparateDeclarations::apply(TIntermNode *root)
+{
+    SeparateDeclarations separateDecl;
+    root->traverse(&separateDecl);
+    separateDecl.updateTree();
+}
+
+SeparateDeclarations::SeparateDeclarations()
+    : TIntermTraverser(true, false, false)
+{
+}
+
+bool SeparateDeclarations::visitAggregate(Visit, TIntermAggregate *node)
+{
+    if (node->getOp() == EOpDeclaration)
+    {
+        TIntermSequence *sequence = node->getSequence();
+        bool sequenceContainsArrays = false;
+        for (size_t ii = 0; ii < sequence->size(); ++ii)
+        {
+            TIntermTyped *typed = sequence->at(ii)->getAsTyped();
+            if (typed != nullptr && typed->isArray())
+            {
+                sequenceContainsArrays = true;
+                break;
+            }
+        }
+        if (sequence->size() > 1 && sequenceContainsArrays)
+        {
+            TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
+            ASSERT(parentAgg != nullptr);
+
+            TIntermSequence replacementDeclarations;
+            for (size_t ii = 0; ii < sequence->size(); ++ii)
+            {
+                TIntermAggregate *replacementDeclaration = new TIntermAggregate;
+
+                replacementDeclaration->setOp(EOpDeclaration);
+                replacementDeclaration->getSequence()->push_back(sequence->at(ii));
+                replacementDeclaration->setLine(sequence->at(ii)->getLine());
+                replacementDeclarations.push_back(replacementDeclaration);
+            }
+
+            mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacementDeclarations));
+        }
+        return false;
+    }
+    return true;
+}
+
+} // namespace
+
+void SeparateArrayDeclarations(TIntermNode *root)
+{
+    SeparateDeclarations::apply(root);
+}
diff --git a/src/compiler/translator/SeparateDeclarations.h b/src/compiler/translator/SeparateDeclarations.h
new file mode 100644
index 0000000..2aa5294
--- /dev/null
+++ b/src/compiler/translator/SeparateDeclarations.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateArrayDeclarations function processes declarations that contain array declarators. Each declarator in
+// such declarations gets its own declaration.
+// This is useful as an intermediate step when initialization needs to be separated from declaration.
+// Example:
+//     int a[1] = int[1](1), b[1] = int[1](2);
+// gets transformed when run through this class into the AST equivalent of:
+//     int a[1] = int[1](1);
+//     int b[1] = int[1](2);
+
+#ifndef COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
+#define COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
+
+class TIntermNode;
+
+void SeparateArrayDeclarations(TIntermNode *root);
+
+#endif  // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
diff --git a/src/compiler/translator/TranslatorHLSL.cpp b/src/compiler/translator/TranslatorHLSL.cpp
index f6275de..d861be1 100644
--- a/src/compiler/translator/TranslatorHLSL.cpp
+++ b/src/compiler/translator/TranslatorHLSL.cpp
@@ -7,6 +7,7 @@
 #include "compiler/translator/TranslatorHLSL.h"
 
 #include "compiler/translator/OutputHLSL.h"
+#include "compiler/translator/SeparateDeclarations.h"
 #include "compiler/translator/SimplifyArrayAssignment.h"
 
 TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
@@ -19,6 +20,8 @@
     const ShBuiltInResources &resources = getResources();
     int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
 
+    SeparateArrayDeclarations(root);
+
     SimplifyArrayAssignment simplify;
     root->traverse(&simplify);