Add basic support for assigning arrays in HLSL output

Implement support for assignments where the return value of the assignment
is not used in another part of the expression.

TEST=WebGL conformance tests
BUG=angleproject:960

Change-Id: Ibf9d71a75d27d139d2aabb5162ab04a0974321d3
Reviewed-on: https://chromium-review.googlesource.com/263222
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index ffc6cf3..c3240f5 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -345,6 +345,14 @@
             out << eqFunction->functionDefinition << "\n";
         }
     }
+    if (!mArrayAssignmentFunctions.empty())
+    {
+        out << "\n// Assignment functions\n\n";
+        for (const auto &assignmentFunction : mArrayAssignmentFunctions)
+        {
+            out << assignmentFunction.functionDefinition << "\n";
+        }
+    }
 
     if (mUsesDiscardRewriting)
     {
@@ -1443,7 +1451,8 @@
       case EOpAssign:
         if (node->getLeft()->isArray())
         {
-            UNIMPLEMENTED();
+            const TString &functionName = addArrayAssignmentFunction(node->getType());
+            outputTriplet(visit, (functionName + "(").c_str(), ", ", ")");
         }
         else
         {
@@ -3022,7 +3031,7 @@
 
     const TString &typeName = TypeString(type);
 
-    ArrayEqualityFunction *function = new ArrayEqualityFunction();
+    ArrayHelperFunction *function = new ArrayHelperFunction();
     function->type = type;
 
     TInfoSinkBase fnNameOut;
@@ -3061,4 +3070,42 @@
     return function->functionName;
 }
 
+TString OutputHLSL::addArrayAssignmentFunction(const TType& type)
+{
+    for (const auto &assignFunction : mArrayAssignmentFunctions)
+    {
+        if (assignFunction.type == type)
+        {
+            return assignFunction.functionName;
+        }
+    }
+
+    const TString &typeName = TypeString(type);
+
+    ArrayHelperFunction function;
+    function.type = type;
+
+    TInfoSinkBase fnNameOut;
+    fnNameOut << "angle_assign_" << type.getArraySize() << "_" << typeName;
+    function.functionName = fnNameOut.c_str();
+
+    TInfoSinkBase fnOut;
+
+    fnOut << "void " << function.functionName << "(out "
+        << typeName << " a[" << type.getArraySize() << "], "
+        << typeName << " b[" << type.getArraySize() << "])\n"
+        << "{\n"
+           "    for (int i = 0; i < " << type.getArraySize() << "; ++i)\n"
+           "    {\n"
+           "        a[i] = b[i];\n"
+           "    }\n"
+           "}\n";
+
+    function.functionDefinition = fnOut.c_str();
+
+    mArrayAssignmentFunctions.push_back(function);
+
+    return function.functionName;
+}
+
 }
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index 4083376..51da877 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -89,6 +89,7 @@
     // Returns the function name
     TString addStructEqualityFunction(const TStructure &structure);
     TString addArrayEqualityFunction(const TType &type);
+    TString addArrayAssignmentFunction(const TType &type);
 
     sh::GLenum mShaderType;
     int mShaderVersion;
@@ -186,31 +187,33 @@
     // these static globals after we initialize our other globals.
     std::vector<std::pair<TIntermSymbol*, TIntermTyped*>> mDeferredGlobalInitializers;
 
-    struct EqualityFunction
+    struct HelperFunction
     {
         TString functionName;
         TString functionDefinition;
 
-        virtual ~EqualityFunction() {}
+        virtual ~HelperFunction() {}
     };
 
     // A list of all equality comparison functions. It's important to preserve the order at
     // which we add the functions, since nested structures call each other recursively, and
     // structure equality functions may need to call array equality functions and vice versa.
     // The ownership of the pointers is maintained by the type-specific arrays.
-    std::vector<EqualityFunction*> mEqualityFunctions;
+    std::vector<HelperFunction*> mEqualityFunctions;
 
-    struct StructEqualityFunction : public EqualityFunction
+    struct StructEqualityFunction : public HelperFunction
     {
         const TStructure *structure;
     };
     std::vector<StructEqualityFunction*> mStructEqualityFunctions;
 
-    struct ArrayEqualityFunction : public EqualityFunction
+    struct ArrayHelperFunction : public HelperFunction
     {
         TType type;
     };
-    std::vector<ArrayEqualityFunction*> mArrayEqualityFunctions;
+    std::vector<ArrayHelperFunction*> mArrayEqualityFunctions;
+
+    std::vector<ArrayHelperFunction> mArrayAssignmentFunctions;
 };
 
 }